Index: /inundation/pmesh/AppShell.py
===================================================================
--- /inundation/pmesh/AppShell.py	(revision 1719)
+++ /inundation/pmesh/AppShell.py	(revision 1719)
@@ -0,0 +1,320 @@
+#! /usr/env/python
+
+"""
+AppShell provides a GUI application framework.
+
+This is a streamlined adaptation of GuiAppD.py, originally
+created by Doug Hellmann (doughellmann@mindspring.com).
+
+"""
+
+from Tkinter import *
+import Pmw
+import sys, string
+import ProgressBar
+
+class AppShell(Pmw.MegaWidget):        
+    appversion      = '1.0'
+    appname         = 'Generic Application Frame'
+    copyright       = 'Copyright YYYY Your Company. All Rights Reserved'
+    contactname     = 'Your Name'
+    contactphone    = '(999) 555-1212'
+    contactemail    = 'youremail@host.com'
+          
+    frameWidth      = 450
+    frameHeight     = 320
+    padx            = 5
+    pady            = 5
+    usecommandarea  = 0
+    balloonhelp     = 1
+    
+    busyCursor = 'watch'
+    
+    def __init__(self, **kw):
+        optiondefs = (
+            ('padx',           1,                   Pmw.INITOPT),
+            ('pady',           1,                   Pmw.INITOPT),
+            ('framewidth',     1,                   Pmw.INITOPT),
+            ('frameheight',    1,                   Pmw.INITOPT),
+            ('usecommandarea', self.usecommandarea, Pmw.INITOPT))
+        self.defineoptions(kw, optiondefs)
+        
+        self.root = Tk()
+        self.initializeTk(self.root)
+        Pmw.initialise(self.root)
+        self.root.title(self.appname)
+        self.root.geometry('%dx%d' % (self.frameWidth, self.frameHeight))
+
+        # Initialize the base class
+        Pmw.MegaWidget.__init__(self, parent=self.root)
+        
+        # initialize the application
+        self.appInit()
+        
+        # create the interface
+        self.__createInterface()
+        
+        # create a table to hold the cursors for
+        # widgets which get changed when we go busy
+        self.preBusyCursors = None
+        
+        # pack the container and set focus
+        # to ourselves
+        self._hull.pack(side=TOP, fill=BOTH, expand=YES)
+        self.focus_set()
+
+        # initialize our options
+        self.initialiseoptions(AppShell)
+        
+    def appInit(self):
+        # Called before interface is created (should be overridden).
+        pass
+        
+    def initializeTk(self, root):
+        # Initialize platform-specific options
+        if sys.platform == 'mac':
+            self.__initializeTk_mac(root)
+        elif sys.platform == 'win32':
+            self.__initializeTk_win32(root)
+        else:
+            self.__initializeTk_unix(root)
+
+    def __initializeTk_colors_common(self, root):
+        root.option_add('*background', 'grey')
+        root.option_add('*foreground', 'black')
+        root.option_add('*EntryField.Entry.background', 'white')
+        root.option_add('*Entry.background', 'white')        
+        root.option_add('*MessageBar.Entry.background', 'gray85')
+        root.option_add('*Listbox*background', 'white')
+        root.option_add('*Listbox*selectBackground', 'dark slate blue')
+        root.option_add('*Listbox*selectForeground', 'white')
+                        
+    def __initializeTk_win32(self, root):
+        self.__initializeTk_colors_common(root)
+        root.option_add('*Font', 'Verdana 10 bold')
+        root.option_add('*EntryField.Entry.Font', 'Courier 10')
+        root.option_add('*Listbox*Font', 'Courier 10')
+        
+    def __initializeTk_mac(self, root):
+        self.__initializeTk_colors_common(root)
+        
+    def __initializeTk_unix(self, root):
+        self.__initializeTk_colors_common(root)
+
+    def busyStart(self, newcursor=None):
+        if not newcursor:
+            newcursor = self.busyCursor
+        newPreBusyCursors = {}
+        for component in self.busyWidgets:
+            newPreBusyCursors[component] = component['cursor']
+            component.configure(cursor=newcursor)
+            component.update_idletasks()
+        self.preBusyCursors = (newPreBusyCursors, self.preBusyCursors)
+        
+    def busyEnd(self):
+        if not self.preBusyCursors:
+            return
+        oldPreBusyCursors = self.preBusyCursors[0]
+        self.preBusyCursors = self.preBusyCursors[1]
+        for component in self.busyWidgets:
+            try:
+                component.configure(cursor=oldPreBusyCursors[component])
+            except KeyError:
+                pass
+            component.update_idletasks()
+              
+    def __createAboutBox(self):
+        Pmw.aboutversion(self.appversion)
+        Pmw.aboutcopyright(self.copyright)
+        Pmw.aboutcontact(
+          'For more information, contact:\n %s\n Phone: %s\n Email: %s' %\
+                      (self.contactname, self.contactphone, 
+                       self.contactemail))
+        self.about = Pmw.AboutDialog(self._hull, 
+                                     applicationname=self.appname)
+        self.about.withdraw()
+        return None
+       
+    def showAbout(self):
+        # Create the dialog to display about and contact information.
+        self.about.show()
+        self.about.focus_set()
+       
+    def toggleBalloon(self):
+	if self.toggleBalloonVar.get():
+            self.__balloon.configure(state = 'both')
+	else:
+            self.__balloon.configure(state = 'status')
+
+    def __createMenuBar(self):
+        self.menuBar = self.createcomponent('menubar', (), None,
+                                            Pmw.MenuBar,
+                                            (self._hull,),
+                                            hull_relief=RAISED,
+                                            hull_borderwidth=1,
+                                            balloon=self.balloon())
+
+        self.menuBar.pack(fill=X)
+        self.menuBar.addmenu('Help', 'About %s' % self.appname, side='right')
+        self.menuBar.addmenu('File', 'File commands and Quit')
+                            
+    def createMenuBar(self):
+        self.menuBar.addmenuitem('Help', 'command',
+                                 'Get information on application', 
+                                 label='About...', command=self.showAbout)
+        self.toggleBalloonVar = IntVar()
+        self.toggleBalloonVar.set(1)
+        self.menuBar.addmenuitem('Help', 'checkbutton',
+                                 'Toggle balloon help',
+                                 label='Balloon help',
+                                 variable = self.toggleBalloonVar,
+                                 command=self.toggleBalloon)
+
+        self.menuBar.addmenuitem('File', 'command', 'Quit this application',
+                                label='Quit',
+                                command=self.quit)
+                                        
+    def __createBalloon(self):
+        # Create the balloon help manager for the frame.
+        # Create the manager for the balloon help
+        self.__balloon = self.createcomponent('balloon', (), None,
+                                              Pmw.Balloon, (self._hull,))
+
+    def balloon(self):
+        return self.__balloon
+
+    def __createDataArea(self):
+        # Create data area where data entry widgets are placed.
+        self.dataArea = self.createcomponent('dataarea',
+                                             (), None,
+                                             Frame, (self._hull,), 
+                                             relief=GROOVE, 
+                                             bd=1)
+        self.dataArea.pack(side=TOP, fill=BOTH, expand=YES,
+                           padx=self['padx'], pady=self['pady'])
+
+    def __createCommandArea(self):
+        # Create a command area for application-wide buttons.
+        self.__commandFrame = self.createcomponent('commandframe', (), None,
+                                                   Frame,
+                                                   (self._hull,),
+                                                   relief=SUNKEN,
+                                                   bd=1)
+        self.__buttonBox = self.createcomponent('buttonbox', (), None,
+                                                Pmw.ButtonBox,
+                                                (self.__commandFrame,),
+                                                padx=0, pady=0)
+        self.__buttonBox.pack(side=TOP, expand=NO, fill=X)
+        if self['usecommandarea']:
+            self.__commandFrame.pack(side=TOP, 
+                                     expand=NO, 
+                                     fill=X,
+                                     padx=self['padx'],
+                                     pady=self['pady'])
+
+
+    def __createMessageBar(self):
+        # Create the message bar area for help and status messages.
+        frame = self.createcomponent('bottomtray', (), None,
+                                     Frame,(self._hull,), relief=SUNKEN)
+        self.__messageBar = self.createcomponent('messagebar',
+                                                  (), None,
+                                                 Pmw.MessageBar, 
+                                                 (frame,),
+                                                 #entry_width = 40,
+                                                 entry_relief=SUNKEN,
+                                                 entry_bd=1,
+                                                 labelpos=None)
+        self.__messageBar.pack(side=LEFT, expand=YES, fill=X)
+
+        self.__progressBar = ProgressBar.ProgressBar(frame,
+                                                fillColor='slateblue',
+                                                doLabel=1,
+                                                width=150)
+        self.__progressBar.frame.pack(side=LEFT, expand=NO, fill=NONE)
+
+        self.updateProgress(0)
+        frame.pack(side=BOTTOM, expand=NO, fill=X)
+                   
+        self.__balloon.configure(statuscommand = \
+                                 self.__messageBar.helpmessage)
+
+    def messageBar(self):
+	return self.__messageBar
+
+    def updateProgress(self, newValue=0, newMax=0):
+        self.__progressBar.updateProgress(newValue, newMax)
+
+    def bind(self, child, balloonHelpMsg, statusHelpMsg=None):
+        # Bind a help message and/or status message to a widget.
+        self.__balloon.bind(child, balloonHelpMsg, statusHelpMsg)
+
+    def interior(self):
+        # Retrieve the interior site where widgets should go.
+        return self.dataArea
+
+    def buttonBox(self):
+        # Retrieve the button box.
+        return self.__buttonBox
+
+    def buttonAdd(self, buttonName, helpMessage=None,
+                  statusMessage=None, **kw):
+        # Add a button to the button box.
+        newBtn = self.__buttonBox.add(buttonName)
+        newBtn.configure(kw)
+        if helpMessage:
+             self.bind(newBtn, helpMessage, statusMessage)
+        return newBtn
+
+    def __createInterface(self):
+        self.__createBalloon()
+        self.__createMenuBar()
+        self.__createDataArea()
+        self.__createCommandArea()
+        self.__createMessageBar()
+        self.__createAboutBox()
+        #
+        # Create the parts of the interface
+        # which can be modified by subclasses
+        #
+        self.busyWidgets = ( self.root, )
+        self.createMenuBar()
+        self.createInterface()
+
+    def createInterface(self):
+        # Override this method to create the interface for the app.
+        pass
+        
+    def main(self):
+        # This method should be left intact!
+        self.pack()
+        self.mainloop()
+        
+    def run(self):
+        self.main()
+
+class TestAppShell(AppShell):
+        usecommandarea=1
+        
+        def createButtons(self):
+                self.buttonAdd('Ok',
+                        helpMessage='Exit',
+                        statusMessage='Exit',
+                        command=self.quit)
+        
+        def createMain(self):
+                self.label = self.createcomponent('label', (), None,
+                                        Label,
+                                        (self.interior(),),
+                                        text='Data Area')
+                self.label.pack()
+                self.bind(self.label, 'Space taker')
+                
+        def createInterface(self):
+                AppShell.createInterface(self)
+                self.createButtons()
+                self.createMain()
+        
+if __name__ == '__main__':
+        test = TestAppShell(balloon_state='both')
+        test.run()
Index: /inundation/pmesh/Pmw.py
===================================================================
--- /inundation/pmesh/Pmw.py	(revision 1719)
+++ /inundation/pmesh/Pmw.py	(revision 1719)
@@ -0,0 +1,9233 @@
+
+import PmwColor
+Color = PmwColor
+del PmwColor
+
+import PmwBlt
+Blt = PmwBlt
+del PmwBlt
+
+
+### Loader functions:
+
+_VERSION = '1.2'
+
+def setversion(version):
+    if version != _VERSION:
+        raise ValueError, 'Dynamic versioning not available'
+
+def setalphaversions(*alpha_versions):
+    if alpha_versions != ():
+	raise ValueError, 'Dynamic versioning not available'
+
+def version(alpha = 0):
+    if alpha:
+        return ()
+    else:
+        return _VERSION
+
+def installedversions(alpha = 0):
+    if alpha:
+        return ()
+    else:
+        return (_VERSION,)
+
+
+######################################################################
+### File: PmwBase.py
+# Pmw megawidget base classes.
+
+# This module provides a foundation for building megawidgets.  It
+# contains the MegaArchetype class which manages component widgets and
+# configuration options.  Also provided are the MegaToplevel and
+# MegaWidget classes, derived from the MegaArchetype class.  The
+# MegaToplevel class contains a Tkinter Toplevel widget to act as the
+# container of the megawidget.  This is used as the base class of all
+# megawidgets that are contained in their own top level window, such
+# as a Dialog window.  The MegaWidget class contains a Tkinter Frame
+# to act as the container of the megawidget.  This is used as the base
+# class of all other megawidgets, such as a ComboBox or ButtonBox.
+#
+# Megawidgets are built by creating a class that inherits from either
+# the MegaToplevel or MegaWidget class.
+
+import os
+import string
+import sys
+import traceback
+import types
+import Tkinter
+
+# Special values used in index() methods of several megawidgets.
+END = ['end']
+SELECT = ['select']
+DEFAULT = ['default']
+
+# Constant used to indicate that an option can only be set by a call
+# to the constructor.
+INITOPT = ['initopt']
+_DEFAULT_OPTION_VALUE = ['default_option_value']
+_useTkOptionDb = 0
+
+# Symbolic constants for the indexes into an optionInfo list.
+_OPT_DEFAULT         = 0
+_OPT_VALUE           = 1
+_OPT_FUNCTION        = 2
+
+# Stacks
+
+_busyStack = []
+    # Stack which tracks nested calls to show/hidebusycursor (called
+    # either directly or from activate()/deactivate()).  Each element
+    # is a dictionary containing:
+    #   'newBusyWindows' :  List of windows which had busy_hold called
+    #                       on them during a call to showbusycursor(). 
+    #                       The corresponding call to hidebusycursor()
+    #                       will call busy_release on these windows.
+    #   'busyFocus' :       The blt _Busy window which showbusycursor()
+    #                       set the focus to.
+    #   'previousFocus' :   The focus as it was when showbusycursor()
+    #                       was called.  The corresponding call to
+    #                       hidebusycursor() will restore this focus if
+    #                       the focus has not been changed from busyFocus.
+
+_grabStack = []
+    # Stack of grabbed windows.  It tracks calls to push/popgrab()
+    # (called either directly or from activate()/deactivate()).  The
+    # window on the top of the stack is the window currently with the
+    # grab.  Each element is a dictionary containing:
+    #   'grabWindow' :      The window grabbed by pushgrab().  The
+    #                       corresponding call to popgrab() will release
+    #                       the grab on this window and restore the grab
+    #                       on the next window in the stack (if there is one).
+    #   'globalMode' :      True if the grabWindow was grabbed with a
+    #                       global grab, false if the grab was local
+    #                       and 'nograb' if no grab was performed.
+    #   'previousFocus' :   The focus as it was when pushgrab()
+    #                       was called.  The corresponding call to
+    #                       popgrab() will restore this focus.
+    #   'deactivateFunction' :
+    #       The function to call (usually grabWindow.deactivate) if
+    #       popgrab() is called (usually from a deactivate() method)
+    #       on a window which is not at the top of the stack (that is,
+    #       does not have the grab or focus).  For example, if a modal
+    #       dialog is deleted by the window manager or deactivated by
+    #       a timer.  In this case, all dialogs above and including
+    #       this one are deactivated, starting at the top of the
+    #       stack.
+
+    # Note that when dealing with focus windows, the name of the Tk
+    # widget is used, since it may be the '_Busy' window, which has no
+    # python instance associated with it.
+
+#=============================================================================
+
+# Functions used to forward methods from a class to a component.
+
+# Fill in a flattened method resolution dictionary for a class (attributes are 
+# filtered out). Flattening honours the MI method resolution rules 
+# (depth-first search of bases in order). The dictionary has method names
+# for keys and functions for values.
+def __methodDict(cls, dict):
+
+    # the strategy is to traverse the class in the _reverse_ of the normal
+    # order, and overwrite any duplicates.
+    baseList = list(cls.__bases__)
+    baseList.reverse()
+    
+    # do bases in reverse order, so first base overrides last base
+    for super in baseList:
+	__methodDict(super, dict)
+
+    # do my methods last to override base classes
+    for key, value in cls.__dict__.items():
+	# ignore class attributes
+	if type(value) == types.FunctionType:
+	    dict[key] = value
+
+def __methods(cls):
+    # Return all method names for a class.
+
+    # Return all method names for a class (attributes are filtered
+    # out).  Base classes are searched recursively.
+
+    dict = {}
+    __methodDict(cls, dict)
+    return dict.keys()
+	
+# Function body to resolve a forwarding given the target method name and the 
+# attribute name. The resulting lambda requires only self, but will forward 
+# any other parameters.
+__stringBody = (
+    'def %(method)s(this, *args, **kw): return ' +
+    'apply(this.%(attribute)s.%(method)s, args, kw)')
+
+# Get a unique id
+__counter = 0
+def __unique():
+  global __counter
+  __counter = __counter + 1
+  return str(__counter)
+
+# Function body to resolve a forwarding given the target method name and the
+# index of the resolution function. The resulting lambda requires only self, 
+# but will forward any other parameters. The target instance is identified 
+# by invoking the resolution function.
+__funcBody = (
+    'def %(method)s(this, *args, **kw): return ' +
+    'apply(this.%(forwardFunc)s().%(method)s, args, kw)')
+
+def forwardmethods(fromClass, toClass, toPart, exclude = ()):
+    # Forward all methods from one class to another.
+
+    # Forwarders will be created in fromClass to forward method
+    # invocations to toClass.  The methods to be forwarded are
+    # identified by flattening the interface of toClass, and excluding
+    # methods identified in the exclude list.  Methods already defined
+    # in fromClass, or special methods with one or more leading or
+    # trailing underscores will not be forwarded.
+
+    # For a given object of class fromClass, the corresponding toClass
+    # object is identified using toPart.  This can either be a String
+    # denoting an attribute of fromClass objects, or a function taking
+    # a fromClass object and returning a toClass object.
+
+    # Example:
+    #     class MyClass:
+    #     ...
+    #         def __init__(self):
+    #             ...
+    #             self.__target = TargetClass()
+    #             ...
+    #         def findtarget(self):
+    #             return self.__target
+    #     forwardmethods(MyClass, TargetClass, '__target', ['dangerous1', 'dangerous2'])
+    #     # ...or...
+    #     forwardmethods(MyClass, TargetClass, MyClass.findtarget, 
+    #             ['dangerous1', 'dangerous2'])
+
+    # In both cases, all TargetClass methods will be forwarded from
+    # MyClass except for dangerous1, dangerous2, special methods like
+    # __str__, and pre-existing methods like findtarget.
+
+
+    # Allow an attribute name (String) or a function to determine the instance
+    if type(toPart) != types.StringType:
+
+	# check that it is something like a function
+	if callable(toPart):
+
+	    # If a method is passed, use the function within it
+	    if hasattr(toPart, 'im_func'):
+		toPart = toPart.im_func
+		
+	    # After this is set up, forwarders in this class will use
+	    # the forwarding function. The forwarding function name is
+	    # guaranteed to be unique, so that it can't be hidden by subclasses
+	    forwardName = '__fwdfunc__' + __unique()
+	    fromClass.__dict__[forwardName] = toPart
+
+	# It's not a valid type
+	else:
+	    raise TypeError, 'toPart must be attribute name, function or method'
+
+    # get the full set of candidate methods
+    dict = {}
+    __methodDict(toClass, dict)
+
+    # discard special methods
+    for ex in dict.keys():
+	if ex[:1] == '_' or ex[-1:] == '_':
+	    del dict[ex]
+    # discard dangerous methods supplied by the caller
+    for ex in exclude:
+	if dict.has_key(ex):
+	    del dict[ex]
+    # discard methods already defined in fromClass
+    for ex in __methods(fromClass):
+	if dict.has_key(ex):
+	    del dict[ex]
+
+    for method, func in dict.items():
+	d = {'method': method, 'func': func}
+	if type(toPart) == types.StringType:
+	    execString = \
+		__stringBody % {'method' : method, 'attribute' : toPart}
+	else:
+	    execString = \
+		__funcBody % {'forwardFunc' : forwardName, 'method' : method}
+
+	exec execString in d
+
+	# this creates a method
+	fromClass.__dict__[method] = d[method]
+
+#=============================================================================
+
+def setgeometryanddeiconify(window, geom):
+    # To avoid flashes on X and to position the window correctly on NT
+    # (caused by Tk bugs).
+
+    if os.name == 'nt' or \
+            (os.name == 'posix' and sys.platform[:6] == 'cygwin'):
+        # Require overrideredirect trick to stop window frame
+        # appearing momentarily.
+        redirect = window.overrideredirect()
+        if not redirect:
+            window.overrideredirect(1)
+        window.deiconify()
+        if geom is not None:
+            window.geometry(geom)
+        # Call update_idletasks to ensure NT moves the window to the
+        # correct position it is raised.
+        window.update_idletasks()
+        window.tkraise()
+        if not redirect:
+            window.overrideredirect(0)
+    else:
+        if geom is not None:
+            window.geometry(geom)
+
+        # Problem!?  Which way around should the following two calls
+        # go?  If deiconify() is called first then I get complaints
+        # from people using the enlightenment or sawfish window
+        # managers that when a dialog is activated it takes about 2
+        # seconds for the contents of the window to appear.  But if
+        # tkraise() is called first then I get complaints from people
+        # using the twm window manager that when a dialog is activated
+        # it appears in the top right corner of the screen and also
+        # takes about 2 seconds to appear.
+
+        #window.tkraise()
+        # Call update_idletasks to ensure certain window managers (eg: 
+        # enlightenment and sawfish) do not cause Tk to delay for
+        # about two seconds before displaying window.
+        #window.update_idletasks()
+        #window.deiconify()
+
+        window.deiconify()
+        if window.overrideredirect():
+            # The window is not under the control of the window manager
+            # and so we need to raise it ourselves.
+            window.tkraise()
+
+#=============================================================================
+
+class MegaArchetype:
+    # Megawidget abstract root class.
+
+    # This class provides methods which are inherited by classes
+    # implementing useful bases (this class doesn't provide a
+    # container widget inside which the megawidget can be built).
+
+    def __init__(self, parent = None, hullClass = None):
+
+	# Mapping from each megawidget option to a list of information
+	# about the option
+	#   - default value
+	#   - current value
+	#   - function to call when the option is initialised in the
+	#     call to initialiseoptions() in the constructor or
+	#     modified via configure().  If this is INITOPT, the
+	#     option is an initialisation option (an option that can
+	#     be set by the call to the constructor but can not be
+	#     used with configure).
+	# This mapping is not initialised here, but in the call to
+	# defineoptions() which precedes construction of this base class.
+	#
+	# self._optionInfo = {}
+
+	# Mapping from each component name to a tuple of information
+	# about the component.
+	#   - component widget instance
+	#   - configure function of widget instance
+	#   - the class of the widget (Frame, EntryField, etc)
+	#   - cget function of widget instance
+	#   - the name of the component group of this component, if any
+	self.__componentInfo = {}
+
+	# Mapping from alias names to the names of components or
+	# sub-components.
+	self.__componentAliases = {}
+
+	# Contains information about the keywords provided to the
+	# constructor.  It is a mapping from the keyword to a tuple
+	# containing:
+	#    - value of keyword
+	#    - a boolean indicating if the keyword has been used.
+	# A keyword is used if, during the construction of a megawidget,
+	#    - it is defined in a call to defineoptions() or addoptions(), or
+	#    - it references, by name, a component of the megawidget, or
+	#    - it references, by group, at least one component
+	# At the end of megawidget construction, a call is made to
+	# initialiseoptions() which reports an error if there are
+	# unused options given to the constructor.
+        #
+        # After megawidget construction, the dictionary contains
+        # keywords which refer to a dynamic component group, so that
+        # these components can be created after megawidget
+        # construction and still use the group options given to the
+        # constructor.
+	#
+	# self._constructorKeywords = {}
+
+        # List of dynamic component groups.  If a group is included in
+        # this list, then it not an error if a keyword argument for
+        # the group is given to the constructor or to configure(), but
+        # no components with this group have been created.
+        # self._dynamicGroups = ()
+
+	if hullClass is None:
+	    self._hull = None
+	else:
+	    if parent is None:
+		parent = Tkinter._default_root
+
+	    # Create the hull.
+	    self._hull = self.createcomponent('hull',
+		    (), None,
+		    hullClass, (parent,))
+	    _hullToMegaWidget[self._hull] = self
+
+	    if _useTkOptionDb:
+		# Now that a widget has been created, query the Tk
+		# option database to get the default values for the
+		# options which have not been set in the call to the
+		# constructor.  This assumes that defineoptions() is
+		# called before the __init__().
+		option_get = self.option_get
+		_VALUE = _OPT_VALUE
+		_DEFAULT = _OPT_DEFAULT
+		for name, info in self._optionInfo.items():
+		    value = info[_VALUE]
+		    if value is _DEFAULT_OPTION_VALUE:
+			resourceClass = string.upper(name[0]) + name[1:]
+			value = option_get(name, resourceClass)
+			if value != '':
+			    try:
+				# Convert the string to int/float/tuple, etc
+				value = eval(value, {'__builtins__': {}})
+			    except:
+				pass
+			    info[_VALUE] = value
+			else:
+			    info[_VALUE] = info[_DEFAULT]
+
+    def destroy(self):
+        # Clean up optionInfo in case it contains circular references
+        # in the function field, such as self._settitle in class
+        # MegaToplevel.
+
+	self._optionInfo = {}
+        if self._hull is not None:
+            del _hullToMegaWidget[self._hull]
+            self._hull.destroy()
+
+    #======================================================================
+    # Methods used (mainly) during the construction of the megawidget.
+
+    def defineoptions(self, keywords, optionDefs, dynamicGroups = ()):
+	# Create options, providing the default value and the method
+	# to call when the value is changed.  If any option created by
+	# base classes has the same name as one in <optionDefs>, the
+	# base class's value and function will be overriden.
+
+	# This should be called before the constructor of the base
+	# class, so that default values defined in the derived class
+	# override those in the base class.
+
+	if not hasattr(self, '_constructorKeywords'):
+            # First time defineoptions has been called.
+	    tmp = {}
+	    for option, value in keywords.items():
+		tmp[option] = [value, 0]
+	    self._constructorKeywords = tmp
+	    self._optionInfo = {}
+	    self._initialiseoptions_counter = 0
+        self._initialiseoptions_counter = self._initialiseoptions_counter + 1
+
+        if not hasattr(self, '_dynamicGroups'):
+            self._dynamicGroups = ()
+        self._dynamicGroups = self._dynamicGroups + tuple(dynamicGroups)
+	self.addoptions(optionDefs)
+
+    def addoptions(self, optionDefs):
+	# Add additional options, providing the default value and the
+	# method to call when the value is changed.  See
+	# "defineoptions" for more details
+
+	# optimisations:
+	optionInfo = self._optionInfo
+	optionInfo_has_key = optionInfo.has_key
+	keywords = self._constructorKeywords
+	keywords_has_key = keywords.has_key
+	FUNCTION = _OPT_FUNCTION
+
+	for name, default, function in optionDefs:
+	    if '_' not in name:
+		# The option will already exist if it has been defined
+		# in a derived class.  In this case, do not override the
+		# default value of the option or the callback function
+		# if it is not None.
+		if not optionInfo_has_key(name):
+		    if keywords_has_key(name):
+			value = keywords[name][0]
+			optionInfo[name] = [default, value, function]
+			del keywords[name]
+		    else:
+			if _useTkOptionDb:
+			    optionInfo[name] = \
+				    [default, _DEFAULT_OPTION_VALUE, function]
+			else:
+			    optionInfo[name] = [default, default, function]
+		elif optionInfo[name][FUNCTION] is None:
+		    optionInfo[name][FUNCTION] = function
+	    else:
+		# This option is of the form "component_option".  If this is
+		# not already defined in self._constructorKeywords add it.
+		# This allows a derived class to override the default value
+		# of an option of a component of a base class.
+		if not keywords_has_key(name):
+		    keywords[name] = [default, 0]
+
+    def createcomponent(self, componentName, componentAliases,
+            componentGroup, widgetClass, *widgetArgs, **kw):
+	# Create a component (during construction or later).
+
+	if self.__componentInfo.has_key(componentName):
+	    raise ValueError, 'Component "%s" already exists' % componentName
+
+	if '_' in componentName:
+	    raise ValueError, \
+                    'Component name "%s" must not contain "_"' % componentName
+
+	if hasattr(self, '_constructorKeywords'):
+	    keywords = self._constructorKeywords
+	else:
+	    keywords = {}
+	for alias, component in componentAliases:
+	    # Create aliases to the component and its sub-components.
+	    index = string.find(component, '_')
+	    if index < 0:
+		self.__componentAliases[alias] = (component, None)
+	    else:
+		mainComponent = component[:index]
+		subComponent = component[(index + 1):]
+		self.__componentAliases[alias] = (mainComponent, subComponent)
+
+	    # Remove aliases from the constructor keyword arguments by
+	    # replacing any keyword arguments that begin with *alias*
+	    # with corresponding keys beginning with *component*.
+
+	    alias = alias + '_'
+	    aliasLen = len(alias)
+	    for option in keywords.keys():
+		if len(option) > aliasLen and option[:aliasLen] == alias:
+		    newkey = component + '_' + option[aliasLen:]
+		    keywords[newkey] = keywords[option]
+		    del keywords[option]
+
+	componentPrefix = componentName + '_'
+	nameLen = len(componentPrefix)
+	for option in keywords.keys():
+	    if len(option) > nameLen and option[:nameLen] == componentPrefix:
+		# The keyword argument refers to this component, so add
+		# this to the options to use when constructing the widget.
+		kw[option[nameLen:]] = keywords[option][0]
+		del keywords[option]
+	    else:
+		# Check if this keyword argument refers to the group
+		# of this component.  If so, add this to the options
+		# to use when constructing the widget.  Mark the
+		# keyword argument as being used, but do not remove it
+		# since it may be required when creating another
+		# component.
+		index = string.find(option, '_')
+		if index >= 0 and componentGroup == option[:index]:
+		    rest = option[(index + 1):]
+		    kw[rest] = keywords[option][0]
+		    keywords[option][1] = 1
+
+	if kw.has_key('pyclass'):
+	    widgetClass = kw['pyclass']
+	    del kw['pyclass']
+	if widgetClass is None:
+	    return None
+        if len(widgetArgs) == 1 and type(widgetArgs[0]) == types.TupleType:
+            # Arguments to the constructor can be specified as either
+            # multiple trailing arguments to createcomponent() or as a
+            # single tuple argument.
+            widgetArgs = widgetArgs[0]
+	widget = apply(widgetClass, widgetArgs, kw)
+	componentClass = widget.__class__.__name__
+	self.__componentInfo[componentName] = (widget, widget.configure,
+		componentClass, widget.cget, componentGroup)
+
+	return widget
+
+    def destroycomponent(self, name):
+	# Remove a megawidget component.
+
+	# This command is for use by megawidget designers to destroy a
+	# megawidget component.
+
+	self.__componentInfo[name][0].destroy()
+	del self.__componentInfo[name]
+
+    def createlabel(self, parent, childCols = 1, childRows = 1):
+
+	labelpos = self['labelpos']
+	labelmargin = self['labelmargin']
+	if labelpos is None:
+	    return
+
+	label = self.createcomponent('label',
+		(), None,
+		Tkinter.Label, (parent,))
+
+	if labelpos[0] in 'ns':
+	    # vertical layout
+	    if labelpos[0] == 'n':
+		row = 0
+		margin = 1
+	    else:
+		row = childRows + 3
+		margin = row - 1
+	    label.grid(column=2, row=row, columnspan=childCols, sticky=labelpos)
+	    parent.grid_rowconfigure(margin, minsize=labelmargin)
+	else:
+	    # horizontal layout
+	    if labelpos[0] == 'w':
+		col = 0
+		margin = 1
+	    else:
+		col = childCols + 3
+		margin = col - 1
+	    label.grid(column=col, row=2, rowspan=childRows, sticky=labelpos)
+	    parent.grid_columnconfigure(margin, minsize=labelmargin)
+
+    def initialiseoptions(self, dummy = None):
+        self._initialiseoptions_counter = self._initialiseoptions_counter - 1
+	if self._initialiseoptions_counter == 0:
+	    unusedOptions = []
+	    keywords = self._constructorKeywords
+	    for name in keywords.keys():
+		used = keywords[name][1]
+		if not used:
+                    # This keyword argument has not been used.  If it
+                    # does not refer to a dynamic group, mark it as
+                    # unused.
+                    index = string.find(name, '_')
+                    if index < 0 or name[:index] not in self._dynamicGroups:
+                        unusedOptions.append(name)
+	    if len(unusedOptions) > 0:
+		if len(unusedOptions) == 1:
+		    text = 'Unknown option "'
+		else:
+		    text = 'Unknown options "'
+		raise KeyError, text + string.join(unusedOptions, ', ') + \
+			'" for ' + self.__class__.__name__
+
+	    # Call the configuration callback function for every option.
+	    FUNCTION = _OPT_FUNCTION
+	    for info in self._optionInfo.values():
+		func = info[FUNCTION]
+		if func is not None and func is not INITOPT:
+		    func()
+
+    #======================================================================
+    # Method used to configure the megawidget.
+
+    def configure(self, option=None, **kw):
+	# Query or configure the megawidget options.
+	#
+	# If not empty, *kw* is a dictionary giving new
+	# values for some of the options of this megawidget or its
+	# components.  For options defined for this megawidget, set
+	# the value of the option to the new value and call the
+	# configuration callback function, if any.  For options of the
+	# form <component>_<option>, where <component> is a component
+	# of this megawidget, call the configure method of the
+	# component giving it the new value of the option.  The
+	# <component> part may be an alias or a component group name.
+	#
+	# If *option* is None, return all megawidget configuration
+	# options and settings.  Options are returned as standard 5
+	# element tuples
+	#
+	# If *option* is a string, return the 5 element tuple for the
+	# given configuration option.
+
+	# First, deal with the option queries.
+	if len(kw) == 0:
+	    # This configure call is querying the values of one or all options.
+	    # Return 5-tuples:
+	    #     (optionName, resourceName, resourceClass, default, value)
+	    if option is None:
+		rtn = {}
+		for option, config in self._optionInfo.items():
+		    resourceClass = string.upper(option[0]) + option[1:]
+		    rtn[option] = (option, option, resourceClass,
+			    config[_OPT_DEFAULT], config[_OPT_VALUE])
+		return rtn
+	    else:
+		config = self._optionInfo[option]
+		resourceClass = string.upper(option[0]) + option[1:]
+		return (option, option, resourceClass, config[_OPT_DEFAULT],
+			config[_OPT_VALUE])
+
+	# optimisations:
+	optionInfo = self._optionInfo
+	optionInfo_has_key = optionInfo.has_key
+	componentInfo = self.__componentInfo
+	componentInfo_has_key = componentInfo.has_key
+	componentAliases = self.__componentAliases
+	componentAliases_has_key = componentAliases.has_key
+	VALUE = _OPT_VALUE
+	FUNCTION = _OPT_FUNCTION
+
+	# This will contain a list of options in *kw* which
+	# are known to this megawidget.
+	directOptions = []
+
+	# This will contain information about the options in
+	# *kw* of the form <component>_<option>, where
+	# <component> is a component of this megawidget.  It is a
+	# dictionary whose keys are the configure method of each
+	# component and whose values are a dictionary of options and
+	# values for the component.
+	indirectOptions = {}
+	indirectOptions_has_key = indirectOptions.has_key
+
+	for option, value in kw.items():
+	    if optionInfo_has_key(option):
+		# This is one of the options of this megawidget. 
+		# Make sure it is not an initialisation option.
+		if optionInfo[option][FUNCTION] is INITOPT:
+		    raise KeyError, \
+			    'Cannot configure initialisation option "' \
+			    + option + '" for ' + self.__class__.__name__
+		optionInfo[option][VALUE] = value
+		directOptions.append(option)
+	    else:
+		index = string.find(option, '_')
+		if index >= 0:
+		    # This option may be of the form <component>_<option>.
+		    component = option[:index]
+		    componentOption = option[(index + 1):]
+
+		    # Expand component alias
+		    if componentAliases_has_key(component):
+			component, subComponent = componentAliases[component]
+			if subComponent is not None:
+			    componentOption = subComponent + '_' \
+				    + componentOption
+
+			# Expand option string to write on error
+			option = component + '_' + componentOption
+
+		    if componentInfo_has_key(component):
+			# Configure the named component
+			componentConfigFuncs = [componentInfo[component][1]]
+		    else:
+			# Check if this is a group name and configure all
+			# components in the group.
+			componentConfigFuncs = []
+			for info in componentInfo.values():
+			    if info[4] == component:
+			        componentConfigFuncs.append(info[1])
+
+                        if len(componentConfigFuncs) == 0 and \
+                                component not in self._dynamicGroups:
+			    raise KeyError, 'Unknown option "' + option + \
+				    '" for ' + self.__class__.__name__
+
+		    # Add the configure method(s) (may be more than
+		    # one if this is configuring a component group)
+		    # and option/value to dictionary.
+		    for componentConfigFunc in componentConfigFuncs:
+			if not indirectOptions_has_key(componentConfigFunc):
+			    indirectOptions[componentConfigFunc] = {}
+			indirectOptions[componentConfigFunc][componentOption] \
+				= value
+		else:
+		    raise KeyError, 'Unknown option "' + option + \
+			    '" for ' + self.__class__.__name__
+
+	# Call the configure methods for any components.
+	map(apply, indirectOptions.keys(),
+		((),) * len(indirectOptions), indirectOptions.values())
+
+	# Call the configuration callback function for each option.
+	for option in directOptions:
+	    info = optionInfo[option]
+	    func = info[_OPT_FUNCTION]
+	    if func is not None:
+	      func()
+
+    def __setitem__(self, key, value):
+        apply(self.configure, (), {key: value})
+
+    #======================================================================
+    # Methods used to query the megawidget.
+
+    def component(self, name):
+	# Return a component widget of the megawidget given the
+	# component's name
+	# This allows the user of a megawidget to access and configure
+	# widget components directly.
+
+	# Find the main component and any subcomponents
+	index = string.find(name, '_')
+	if index < 0:
+	    component = name
+	    remainingComponents = None
+	else:
+	    component = name[:index]
+	    remainingComponents = name[(index + 1):]
+
+	# Expand component alias
+	if self.__componentAliases.has_key(component):
+	    component, subComponent = self.__componentAliases[component]
+	    if subComponent is not None:
+		if remainingComponents is None:
+		    remainingComponents = subComponent
+		else:
+		    remainingComponents = subComponent + '_' \
+			    + remainingComponents
+
+	widget = self.__componentInfo[component][0]
+	if remainingComponents is None:
+	    return widget
+	else:
+	    return widget.component(remainingComponents)
+
+    def interior(self):
+	return self._hull
+
+    def hulldestroyed(self):
+	return not _hullToMegaWidget.has_key(self._hull)
+
+    def __str__(self):
+	return str(self._hull)
+
+    def cget(self, option):
+	# Get current configuration setting.
+
+	# Return the value of an option, for example myWidget['font']. 
+
+	if self._optionInfo.has_key(option):
+	    return self._optionInfo[option][_OPT_VALUE]
+	else:
+	    index = string.find(option, '_')
+	    if index >= 0:
+		component = option[:index]
+		componentOption = option[(index + 1):]
+
+		# Expand component alias
+		if self.__componentAliases.has_key(component):
+		    component, subComponent = self.__componentAliases[component]
+		    if subComponent is not None:
+			componentOption = subComponent + '_' + componentOption
+
+		    # Expand option string to write on error
+		    option = component + '_' + componentOption
+
+		if self.__componentInfo.has_key(component):
+		    # Call cget on the component.
+		    componentCget = self.__componentInfo[component][3]
+		    return componentCget(componentOption)
+		else:
+		    # If this is a group name, call cget for one of
+		    # the components in the group.
+		    for info in self.__componentInfo.values():
+			if info[4] == component:
+			    componentCget = info[3]
+			    return componentCget(componentOption)
+
+	raise KeyError, 'Unknown option "' + option + \
+		'" for ' + self.__class__.__name__
+
+    __getitem__ = cget
+
+    def isinitoption(self, option):
+	return self._optionInfo[option][_OPT_FUNCTION] is INITOPT
+
+    def options(self):
+	options = []
+	if hasattr(self, '_optionInfo'):
+	    for option, info in self._optionInfo.items():
+		isinit = info[_OPT_FUNCTION] is INITOPT
+		default = info[_OPT_DEFAULT]
+		options.append((option, default, isinit))
+	    options.sort()
+	return options
+
+    def components(self):
+	# Return a list of all components.
+
+	# This list includes the 'hull' component and all widget subcomponents
+
+	names = self.__componentInfo.keys()
+	names.sort()
+	return names
+
+    def componentaliases(self):
+	# Return a list of all component aliases.
+
+	componentAliases = self.__componentAliases
+
+	names = componentAliases.keys()
+	names.sort()
+	rtn = []
+	for alias in names:
+	    (mainComponent, subComponent) = componentAliases[alias]
+	    if subComponent is None:
+		rtn.append((alias, mainComponent))
+	    else:
+		rtn.append((alias, mainComponent + '_' + subComponent))
+	    
+	return rtn
+
+    def componentgroup(self, name):
+	return self.__componentInfo[name][4]
+
+#=============================================================================
+
+# The grab functions are mainly called by the activate() and
+# deactivate() methods.
+#
+# Use pushgrab() to add a new window to the grab stack.  This
+# releases the grab by the window currently on top of the stack (if
+# there is one) and gives the grab and focus to the new widget.
+#
+# To remove the grab from the window on top of the grab stack, call
+# popgrab().
+#
+# Use releasegrabs() to release the grab and clear the grab stack.
+
+def pushgrab(grabWindow, globalMode, deactivateFunction):
+    prevFocus = grabWindow.tk.call('focus')
+    grabInfo = {
+        'grabWindow' : grabWindow,
+        'globalMode' : globalMode,
+        'previousFocus' : prevFocus,
+        'deactivateFunction' : deactivateFunction,
+    }
+    _grabStack.append(grabInfo)
+    _grabtop()
+    grabWindow.focus_set()
+
+def popgrab(window):
+    # Return the grab to the next window in the grab stack, if any.
+
+    # If this window is not at the top of the grab stack, then it has
+    # just been deleted by the window manager or deactivated by a
+    # timer.  Call the deactivate method for the modal dialog above
+    # this one on the stack. 
+    if _grabStack[-1]['grabWindow'] != window:
+        for index in range(len(_grabStack)):
+            if _grabStack[index]['grabWindow'] == window:
+                _grabStack[index + 1]['deactivateFunction']()
+                break
+
+    grabInfo = _grabStack[-1]
+    del _grabStack[-1]
+
+    topWidget = grabInfo['grabWindow']
+    prevFocus = grabInfo['previousFocus']
+    globalMode = grabInfo['globalMode']
+
+    if globalMode != 'nograb':
+        topWidget.grab_release()
+
+    if len(_grabStack) > 0:
+        _grabtop()
+    if prevFocus != '':
+        try:
+            topWidget.tk.call('focus', prevFocus)
+        except Tkinter.TclError:
+            # Previous focus widget has been deleted. Set focus
+            # to root window.
+            Tkinter._default_root.focus_set()
+    else:
+        # Make sure that focus does not remain on the released widget.
+        if len(_grabStack) > 0:
+            topWidget = _grabStack[-1]['grabWindow']
+            topWidget.focus_set()
+        else:
+            Tkinter._default_root.focus_set()
+
+def grabstacktopwindow():
+    if len(_grabStack) == 0:
+        return None
+    else:
+        return _grabStack[-1]['grabWindow']
+
+def releasegrabs():
+    # Release grab and clear the grab stack.
+
+    current = Tkinter._default_root.grab_current()
+    if current is not None:
+        current.grab_release()
+    _grabStack[:] = []
+
+def _grabtop():
+    grabInfo = _grabStack[-1]
+    topWidget = grabInfo['grabWindow']
+    globalMode = grabInfo['globalMode']
+
+    if globalMode == 'nograb':
+        return
+
+    while 1:
+        try:
+            if globalMode:
+                topWidget.grab_set_global()
+            else:
+                topWidget.grab_set()
+            break
+        except Tkinter.TclError:
+            # Another application has grab.  Keep trying until
+            # grab can succeed.
+            topWidget.after(100)
+
+#=============================================================================
+
+class MegaToplevel(MegaArchetype):
+
+    def __init__(self, parent = None, **kw):
+	# Define the options for this megawidget.
+	optiondefs = (
+            ('activatecommand',   None,                     None),
+            ('deactivatecommand', None,                     None),
+            ('master',            None,                     None),
+            ('title',             None,                     self._settitle),
+            ('hull_class',        self.__class__.__name__,  None),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaArchetype.__init__(self, parent, Tkinter.Toplevel)
+
+	# Initialise instance.
+
+        # Set WM_DELETE_WINDOW protocol, deleting any old callback, so
+        # memory does not leak.
+        if hasattr(self._hull, '_Pmw_WM_DELETE_name'):
+            self._hull.tk.deletecommand(self._hull._Pmw_WM_DELETE_name)
+        self._hull._Pmw_WM_DELETE_name = \
+                self.register(self._userDeleteWindow, needcleanup = 0)
+	self.protocol('WM_DELETE_WINDOW', self._hull._Pmw_WM_DELETE_name)
+
+	# Initialise instance variables.
+
+	self._firstShowing = 1
+	# Used by show() to ensure window retains previous position on screen.
+
+	# The IntVar() variable to wait on during a modal dialog.
+	self._wait = None
+
+	self._active = 0
+	self._userDeleteFunc = self.destroy
+	self._userModalDeleteFunc = self.deactivate
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def _settitle(self):
+	title = self['title']
+	if title is not None:
+	    self.title(title)
+
+    def userdeletefunc(self, func=None):
+        if func:
+	    self._userDeleteFunc = func
+	else:
+	    return self._userDeleteFunc
+
+    def usermodaldeletefunc(self, func=None):
+        if func:
+	    self._userModalDeleteFunc = func
+	else:
+	    return self._userModalDeleteFunc
+
+    def _userDeleteWindow(self):
+	if self.active():
+	    self._userModalDeleteFunc()
+	else:
+	    self._userDeleteFunc()
+
+    def destroy(self):
+	# Allow this to be called more than once.
+	if _hullToMegaWidget.has_key(self._hull):
+	    self.deactivate()
+
+            # Remove circular references, so that object can get cleaned up.
+            del self._userDeleteFunc
+            del self._userModalDeleteFunc
+
+            MegaArchetype.destroy(self)
+
+    def show(self, master = None):
+	if self.state() != 'normal':
+	    if self._firstShowing:
+		# Just let the window manager determine the window
+		# position for the first time.
+		geom = None
+	    else:
+		# Position the window at the same place it was last time.
+		geom = self._sameposition()
+            setgeometryanddeiconify(self, geom)
+
+        if self._firstShowing:
+            self._firstShowing = 0
+        else:
+            if self.transient() == '':
+                self.tkraise()
+
+        # Do this last, otherwise get flashing on NT:
+        if master is not None:
+            if master == 'parent':
+                parent = self.winfo_parent()
+                # winfo_parent() should return the parent widget, but the
+                # the current version of Tkinter returns a string.
+                if type(parent) == types.StringType:
+                    parent = self._hull._nametowidget(parent)
+                master = parent.winfo_toplevel()
+            self.transient(master)
+
+        self.focus()
+
+    def _centreonscreen(self):
+	# Centre the window on the screen.  (Actually halfway across
+	# and one third down.)
+
+        parent = self.winfo_parent()
+        if type(parent) == types.StringType:
+            parent = self._hull._nametowidget(parent)
+
+        # Find size of window.
+	self.update_idletasks()
+        width = self.winfo_width()
+        height = self.winfo_height()
+        if width == 1 and height == 1:
+            # If the window has not yet been displayed, its size is
+            # reported as 1x1, so use requested size.
+            width = self.winfo_reqwidth()
+            height = self.winfo_reqheight()
+
+        # Place in centre of screen:
+	x = (self.winfo_screenwidth() - width) / 2 - parent.winfo_vrootx()
+	y = (self.winfo_screenheight() - height) / 3 - parent.winfo_vrooty()
+	if x < 0:
+	    x = 0
+	if y < 0:
+	    y = 0
+        return '+%d+%d' % (x, y)
+
+    def _sameposition(self):
+	# Position the window at the same place it was last time.
+
+	geometry = self.geometry()
+	index = string.find(geometry, '+')
+	if index >= 0:
+	    return geometry[index:]
+        else:
+	    return None
+
+    def activate(self, globalMode = 0, geometry = 'centerscreenfirst'):
+	if self._active:
+	    raise ValueError, 'Window is already active'
+	if self.state() == 'normal':
+	    self.withdraw()
+
+	self._active = 1
+
+	showbusycursor()
+
+	if self._wait is None:
+	    self._wait = Tkinter.IntVar()
+	self._wait.set(0)
+
+	if geometry == 'centerscreenalways':
+	    geom = self._centreonscreen()
+	elif geometry == 'centerscreenfirst':
+	    if self._firstShowing:
+		# Centre the window the first time it is displayed.
+		geom = self._centreonscreen()
+	    else:
+		# Position the window at the same place it was last time.
+		geom = self._sameposition()
+	elif geometry[:5] == 'first':
+	    if self._firstShowing:
+                geom = geometry[5:]
+	    else:
+		# Position the window at the same place it was last time.
+		geom = self._sameposition()
+        else:
+            geom = geometry
+
+	self._firstShowing = 0
+
+        setgeometryanddeiconify(self, geom)
+
+        # Do this last, otherwise get flashing on NT:
+        master = self['master']
+        if master is not None:
+            if master == 'parent':
+                parent = self.winfo_parent()
+                # winfo_parent() should return the parent widget, but the
+                # the current version of Tkinter returns a string.
+                if type(parent) == types.StringType:
+                    parent = self._hull._nametowidget(parent)
+                master = parent.winfo_toplevel()
+            self.transient(master)
+
+        pushgrab(self._hull, globalMode, self.deactivate)
+	command = self['activatecommand']
+	if callable(command):
+	    command()
+	self.wait_variable(self._wait)
+
+	return self._result
+
+    def deactivate(self, result=None):
+	if not self._active:
+	    return
+	self._active = 0
+
+        # Restore the focus before withdrawing the window, since
+        # otherwise the window manager may take the focus away so we
+        # can't redirect it.  Also, return the grab to the next active
+        # window in the stack, if any.
+        popgrab(self._hull)
+
+        command = self['deactivatecommand']
+        if callable(command):
+            command()
+
+        self.withdraw()
+        hidebusycursor(forceFocusRestore = 1)
+
+        self._result = result
+        self._wait.set(1)
+
+    def active(self):
+	return self._active
+
+forwardmethods(MegaToplevel, Tkinter.Toplevel, '_hull')
+
+#=============================================================================
+
+class MegaWidget(MegaArchetype):
+    def __init__(self, parent = None, **kw):
+	# Define the options for this megawidget.
+	optiondefs = (
+	    ('hull_class',       self.__class__.__name__,  None),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaArchetype.__init__(self, parent, Tkinter.Frame)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+forwardmethods(MegaWidget, Tkinter.Frame, '_hull')
+
+#=============================================================================
+
+# Public functions
+#-----------------
+
+_traceTk = 0
+def tracetk(root = None, on = 1, withStackTrace = 0, file=None):
+    global _withStackTrace
+    global _traceTkFile
+    global _traceTk
+
+    if root is None:
+        root = Tkinter._default_root
+
+    _withStackTrace = withStackTrace
+    _traceTk = on
+    if on:
+	if hasattr(root.tk, '__class__'):
+	    # Tracing already on
+	    return
+	if file is None:
+	    _traceTkFile = sys.stderr
+	else:
+	    _traceTkFile = file
+	tk = _TraceTk(root.tk)
+    else:
+	if not hasattr(root.tk, '__class__'):
+	    # Tracing already off
+	    return
+	tk = root.tk.getTclInterp()
+    _setTkInterps(root, tk)
+
+def showbusycursor():
+
+    _addRootToToplevelBusyInfo()
+    root = Tkinter._default_root
+
+    busyInfo = {
+        'newBusyWindows' : [],
+        'previousFocus' : None,
+        'busyFocus' : None,
+    }
+    _busyStack.append(busyInfo)
+
+    if _disableKeyboardWhileBusy:
+        # Remember the focus as it is now, before it is changed.
+        busyInfo['previousFocus'] = root.tk.call('focus')
+
+    if not _havebltbusy(root):
+        # No busy command, so don't call busy hold on any windows.
+        return
+
+    for (window, winInfo) in _toplevelBusyInfo.items():
+	if (window.state() != 'withdrawn' and not winInfo['isBusy']
+                and not winInfo['excludeFromBusy']):
+            busyInfo['newBusyWindows'].append(window)
+            winInfo['isBusy'] = 1
+            _busy_hold(window, winInfo['busyCursorName'])
+
+            # Make sure that no events for the busy window get
+            # through to Tkinter, otherwise it will crash in
+            # _nametowidget with a 'KeyError: _Busy' if there is
+            # a binding on the toplevel window.
+            window.tk.call('bindtags', winInfo['busyWindow'], 'Pmw_Dummy_Tag')
+
+            if _disableKeyboardWhileBusy:
+                # Remember previous focus widget for this toplevel window
+                # and set focus to the busy window, which will ignore all
+                # keyboard events.
+                winInfo['windowFocus'] = \
+                        window.tk.call('focus', '-lastfor', window._w)
+                window.tk.call('focus', winInfo['busyWindow'])
+                busyInfo['busyFocus'] = winInfo['busyWindow']
+
+    if len(busyInfo['newBusyWindows']) > 0:
+        if os.name == 'nt':
+            # NT needs an "update" before it will change the cursor.
+            window.update()
+        else:
+            window.update_idletasks()
+
+def hidebusycursor(forceFocusRestore = 0):
+
+    # Remember the focus as it is now, before it is changed.
+    root = Tkinter._default_root
+    if _disableKeyboardWhileBusy:
+        currentFocus = root.tk.call('focus')
+
+    # Pop the busy info off the stack.
+    busyInfo = _busyStack[-1]
+    del _busyStack[-1]
+
+    for window in busyInfo['newBusyWindows']:
+        # If this window has not been deleted, release the busy cursor.
+        if _toplevelBusyInfo.has_key(window):
+            winInfo = _toplevelBusyInfo[window]
+            winInfo['isBusy'] = 0
+            _busy_release(window)
+
+            if _disableKeyboardWhileBusy:
+                # Restore previous focus window for this toplevel window,
+                # but only if is still set to the busy window (it may have
+                # been changed).
+                windowFocusNow = window.tk.call('focus', '-lastfor', window._w)
+                if windowFocusNow == winInfo['busyWindow']:
+                    try:
+                        window.tk.call('focus', winInfo['windowFocus'])
+                    except Tkinter.TclError:
+                        # Previous focus widget has been deleted. Set focus
+                        # to toplevel window instead (can't leave focus on
+                        # busy window).
+                        window.focus_set()
+
+    if _disableKeyboardWhileBusy:
+        # Restore the focus, depending on whether the focus had changed
+        # between the calls to showbusycursor and hidebusycursor.
+        if forceFocusRestore or busyInfo['busyFocus'] == currentFocus:
+            # The focus had not changed, so restore it to as it was before
+            # the call to showbusycursor,
+            previousFocus = busyInfo['previousFocus']
+            if previousFocus is not None:
+                try:
+                    root.tk.call('focus', previousFocus)
+                except Tkinter.TclError:
+                    # Previous focus widget has been deleted; forget it.
+                    pass
+        else:
+            # The focus had changed, so restore it to what it had been
+            # changed to before the call to hidebusycursor.
+            root.tk.call('focus', currentFocus)
+
+def clearbusycursor():
+    while len(_busyStack) > 0:
+        hidebusycursor()
+
+def setbusycursorattributes(window, **kw):
+    _addRootToToplevelBusyInfo()
+    for name, value in kw.items():
+        if name == 'exclude':
+            _toplevelBusyInfo[window]['excludeFromBusy'] = value
+        elif name == 'cursorName':
+            _toplevelBusyInfo[window]['busyCursorName'] = value
+        else:
+            raise KeyError, 'Unknown busycursor attribute "' + name + '"'
+
+def _addRootToToplevelBusyInfo():
+    # Include the Tk root window in the list of toplevels.  This must
+    # not be called before Tkinter has had a chance to be initialised by
+    # the application.
+
+    root = Tkinter._default_root
+    if root == None:
+        root = Tkinter.Tk()
+    if not _toplevelBusyInfo.has_key(root):
+        _addToplevelBusyInfo(root)
+
+def busycallback(command, updateFunction = None):
+    if not callable(command):
+	raise ValueError, \
+	    'cannot register non-command busy callback %s %s' % \
+	        (repr(command), type(command))
+    wrapper = _BusyWrapper(command, updateFunction)
+    return wrapper.callback
+
+_errorReportFile = None
+_errorWindow = None
+
+def reporterrorstofile(file = None):
+    global _errorReportFile
+    _errorReportFile = file
+
+def displayerror(text):
+    global _errorWindow
+
+    if _errorReportFile is not None:
+	_errorReportFile.write(text + '\n')
+    else:
+        # Print error on standard error as well as to error window. 
+        # Useful if error window fails to be displayed, for example
+        # when exception is triggered in a <Destroy> binding for root
+        # window.
+        sys.stderr.write(text + '\n')
+
+	if _errorWindow is None:
+	    # The error window has not yet been created.
+	    _errorWindow = _ErrorWindow()
+
+	_errorWindow.showerror(text)
+
+_root = None
+_disableKeyboardWhileBusy = 1
+
+def initialise(
+        root = None,
+        size = None,
+        fontScheme = None,
+        useTkOptionDb = 0,
+        noBltBusy = 0,
+        disableKeyboardWhileBusy = None,
+):
+    # Remember if show/hidebusycursor should ignore keyboard events.
+    global _disableKeyboardWhileBusy
+    if disableKeyboardWhileBusy is not None:
+        _disableKeyboardWhileBusy = disableKeyboardWhileBusy
+
+    # Do not use blt busy command if noBltBusy is set.  Otherwise,
+    # use blt busy if it is available.
+    global _haveBltBusy
+    if noBltBusy:
+        _haveBltBusy = 0
+
+    # Save flag specifying whether the Tk option database should be
+    # queried when setting megawidget option default values.
+    global _useTkOptionDb
+    _useTkOptionDb = useTkOptionDb
+
+    # If we haven't been given a root window, use the default or
+    # create one.
+    if root is None:
+	if Tkinter._default_root is None:
+	    root = Tkinter.Tk()
+	else:
+	    root = Tkinter._default_root
+
+    # If this call is initialising a different Tk interpreter than the
+    # last call, then re-initialise all global variables.  Assume the
+    # last interpreter has been destroyed - ie:  Pmw does not (yet)
+    # support multiple simultaneous interpreters.
+    global _root
+    if _root is not None and _root != root:
+        global _busyStack
+        global _errorWindow
+        global _grabStack
+        global _hullToMegaWidget
+        global _toplevelBusyInfo
+        _busyStack = []
+        _errorWindow = None
+        _grabStack = []
+        _hullToMegaWidget = {}
+        _toplevelBusyInfo = {}
+    _root = root
+
+    # Trap Tkinter Toplevel constructors so that a list of Toplevels
+    # can be maintained.
+    Tkinter.Toplevel.title = __TkinterToplevelTitle
+
+    # Trap Tkinter widget destruction so that megawidgets can be
+    # destroyed when their hull widget is destoyed and the list of
+    # Toplevels can be pruned.
+    Tkinter.Toplevel.destroy = __TkinterToplevelDestroy
+    Tkinter.Widget.destroy = __TkinterWidgetDestroy
+
+    # Modify Tkinter's CallWrapper class to improve the display of
+    # errors which occur in callbacks.
+    Tkinter.CallWrapper = __TkinterCallWrapper
+
+    # Make sure we get to know when the window manager deletes the
+    # root window.  Only do this if the protocol has not yet been set. 
+    # This is required if there is a modal dialog displayed and the
+    # window manager deletes the root window.  Otherwise the
+    # application will not exit, even though there are no windows.
+    if root.protocol('WM_DELETE_WINDOW') == '':
+	root.protocol('WM_DELETE_WINDOW', root.destroy)
+
+    # Set the base font size for the application and set the
+    # Tk option database font resources.
+    
+    _font_initialise(root, size, fontScheme)
+
+    return root
+
+def alignlabels(widgets, sticky = None):
+    if len(widgets) == 0:
+    	return
+
+    widgets[0].update_idletasks()
+
+    # Determine the size of the maximum length label string.
+    maxLabelWidth = 0
+    for iwid in widgets:
+	labelWidth = iwid.grid_bbox(0, 1)[2]
+	if labelWidth > maxLabelWidth:
+	    maxLabelWidth = labelWidth
+
+    # Adjust the margins for the labels such that the child sites and
+    # labels line up.
+    for iwid in widgets:
+	if sticky is not None:
+	    iwid.component('label').grid(sticky=sticky)
+	iwid.grid_columnconfigure(0, minsize = maxLabelWidth)
+#=============================================================================
+
+# Private routines
+#-----------------
+_callToTkReturned = 1
+_recursionCounter = 1
+
+class _TraceTk:
+    def __init__(self, tclInterp):
+        self.tclInterp = tclInterp
+
+    def getTclInterp(self):
+        return self.tclInterp
+
+    # Calling from python into Tk.
+    def call(self, *args, **kw):
+        global _callToTkReturned
+        global _recursionCounter
+
+        _callToTkReturned = 0
+        if len(args) == 1 and type(args[0]) == types.TupleType:
+            argStr = str(args[0])
+        else:
+            argStr = str(args)
+	_traceTkFile.write('CALL  TK> %d:%s%s' %
+                (_recursionCounter, '  ' * _recursionCounter, argStr))
+	_recursionCounter = _recursionCounter + 1
+        try:
+            result = apply(self.tclInterp.call, args, kw)
+	except Tkinter.TclError, errorString:
+            _callToTkReturned = 1
+            _recursionCounter = _recursionCounter - 1
+            _traceTkFile.write('\nTK ERROR> %d:%s-> %s\n' %
+                    (_recursionCounter, '  ' * _recursionCounter,
+                            repr(errorString)))
+            if _withStackTrace:
+                _traceTkFile.write('CALL  TK> stack:\n')
+                traceback.print_stack()
+            raise Tkinter.TclError, errorString
+
+        _recursionCounter = _recursionCounter - 1
+        if _callToTkReturned:
+            _traceTkFile.write('CALL RTN> %d:%s-> %s' %
+                    (_recursionCounter, '  ' * _recursionCounter, repr(result)))
+        else:
+            _callToTkReturned = 1
+            if result:
+                _traceTkFile.write(' -> %s' % repr(result))
+        _traceTkFile.write('\n')
+        if _withStackTrace:
+            _traceTkFile.write('CALL  TK> stack:\n')
+            traceback.print_stack()
+
+        _traceTkFile.flush()
+        return result
+
+    def __getattr__(self, key):
+        return getattr(self.tclInterp, key)
+
+def _setTkInterps(window, tk):
+    window.tk = tk
+    for child in window.children.values():
+      _setTkInterps(child, tk)
+
+#=============================================================================
+
+# Functions to display a busy cursor.  Keep a list of all toplevels
+# and display the busy cursor over them.  The list will contain the Tk
+# root toplevel window as well as all other toplevel windows.
+# Also keep a list of the widget which last had focus for each
+# toplevel.
+
+# Map from toplevel windows to
+#     {'isBusy', 'windowFocus', 'busyWindow',
+#         'excludeFromBusy', 'busyCursorName'}
+_toplevelBusyInfo = {}
+
+# Pmw needs to know all toplevel windows, so that it can call blt busy
+# on them.  This is a hack so we get notified when a Tk topevel is
+# created.  Ideally, the __init__ 'method' should be overridden, but
+# it is a 'read-only special attribute'.  Luckily, title() is always
+# called from the Tkinter Toplevel constructor.
+
+def _addToplevelBusyInfo(window):
+    if window._w == '.':
+        busyWindow = '._Busy'
+    else:
+        busyWindow = window._w + '._Busy'
+
+    _toplevelBusyInfo[window] = {
+        'isBusy' : 0,
+        'windowFocus' : None,
+        'busyWindow' : busyWindow,
+        'excludeFromBusy' : 0,
+        'busyCursorName' : None,
+    }
+
+def __TkinterToplevelTitle(self, *args):
+    # If this is being called from the constructor, include this
+    # Toplevel in the list of toplevels and set the initial
+    # WM_DELETE_WINDOW protocol to destroy() so that we get to know
+    # about it.
+    if not _toplevelBusyInfo.has_key(self):
+        _addToplevelBusyInfo(self)
+        self._Pmw_WM_DELETE_name = self.register(self.destroy, None, 0)
+	self.protocol('WM_DELETE_WINDOW', self._Pmw_WM_DELETE_name)
+
+    return apply(Tkinter.Wm.title, (self,) + args)
+
+_haveBltBusy = None
+def _havebltbusy(window):
+    global _busy_hold, _busy_release, _haveBltBusy
+    if _haveBltBusy is None:
+        import PmwBlt
+        _haveBltBusy = PmwBlt.havebltbusy(window)
+        _busy_hold = PmwBlt.busy_hold
+        if os.name == 'nt':
+            # There is a bug in Blt 2.4i on NT where the busy window
+            # does not follow changes in the children of a window.
+            # Using forget works around the problem.
+            _busy_release = PmwBlt.busy_forget
+        else:
+            _busy_release = PmwBlt.busy_release
+    return _haveBltBusy
+
+class _BusyWrapper:
+    def __init__(self, command, updateFunction):
+	self._command = command
+	self._updateFunction = updateFunction
+
+    def callback(self, *args):
+	showbusycursor()
+	rtn = apply(self._command, args)
+
+	# Call update before hiding the busy windows to clear any
+	# events that may have occurred over the busy windows.
+	if callable(self._updateFunction):
+	    self._updateFunction()
+
+	hidebusycursor()
+	return rtn
+
+#=============================================================================
+
+def drawarrow(canvas, color, direction, tag, baseOffset = 0.25, edgeOffset = 0.15):
+    canvas.delete(tag)
+
+    bw = (string.atoi(canvas['borderwidth']) + 
+            string.atoi(canvas['highlightthickness']))
+    width = string.atoi(canvas['width'])
+    height = string.atoi(canvas['height'])
+
+    if direction in ('up', 'down'):
+        majorDimension = height
+        minorDimension = width
+    else:
+        majorDimension = width
+        minorDimension = height
+
+    offset = round(baseOffset * majorDimension)
+    if direction in ('down', 'right'):
+        base = bw + offset
+        apex = bw + majorDimension - offset
+    else:
+        base = bw + majorDimension - offset
+        apex = bw + offset
+
+    if minorDimension > 3 and minorDimension % 2 == 0:
+        minorDimension = minorDimension - 1
+    half = int(minorDimension * (1 - 2 * edgeOffset)) / 2
+    low = round(bw + edgeOffset * minorDimension)
+    middle = low + half
+    high = low + 2 * half
+
+    if direction in ('up', 'down'):
+        coords = (low, base, high, base, middle, apex)
+    else:
+        coords = (base, low, base, high, apex, middle)
+    kw = {'fill' : color, 'outline' : color, 'tag' : tag}
+    apply(canvas.create_polygon, coords, kw)
+
+#=============================================================================
+
+# Modify the Tkinter destroy methods so that it notifies us when a Tk
+# toplevel or frame is destroyed.
+
+# A map from the 'hull' component of a megawidget to the megawidget. 
+# This is used to clean up a megawidget when its hull is destroyed.
+_hullToMegaWidget = {}
+
+def __TkinterToplevelDestroy(tkWidget):
+    if _hullToMegaWidget.has_key(tkWidget):
+        mega = _hullToMegaWidget[tkWidget]
+        try:
+	    mega.destroy()
+        except:
+	    _reporterror(mega.destroy, ())
+    else:
+        # Delete the busy info structure for this toplevel (if the
+        # window was created before initialise() was called, it
+        # will not have any.
+        if _toplevelBusyInfo.has_key(tkWidget):
+            del _toplevelBusyInfo[tkWidget]
+        if hasattr(tkWidget, '_Pmw_WM_DELETE_name'):
+            tkWidget.tk.deletecommand(tkWidget._Pmw_WM_DELETE_name)
+            del tkWidget._Pmw_WM_DELETE_name
+        Tkinter.BaseWidget.destroy(tkWidget)
+
+def __TkinterWidgetDestroy(tkWidget):
+    if _hullToMegaWidget.has_key(tkWidget):
+        mega = _hullToMegaWidget[tkWidget]
+        try:
+	    mega.destroy()
+        except:
+	    _reporterror(mega.destroy, ())
+    else:
+        Tkinter.BaseWidget.destroy(tkWidget)
+
+#=============================================================================
+
+# Add code to Tkinter to improve the display of errors which occur in
+# callbacks.
+
+class __TkinterCallWrapper:
+    def __init__(self, func, subst, widget):
+	self.func = func
+	self.subst = subst
+	self.widget = widget
+
+    # Calling back from Tk into python.
+    def __call__(self, *args):
+	try:
+	    if self.subst:
+		args = apply(self.subst, args)
+            if _traceTk:
+                if not _callToTkReturned:
+                    _traceTkFile.write('\n')
+                if hasattr(self.func, 'im_class'):
+                    name = self.func.im_class.__name__ + '.' + \
+                        self.func.__name__
+                else:
+                    name = self.func.__name__
+                if len(args) == 1 and hasattr(args[0], 'type'):
+                    # The argument to the callback is an event.
+                    eventName = _eventTypeToName[string.atoi(args[0].type)]
+                    if eventName in ('KeyPress', 'KeyRelease',):
+                        argStr = '(%s %s Event: %s)' % \
+                            (eventName, args[0].keysym, args[0].widget)
+                    else:
+                        argStr = '(%s Event, %s)' % (eventName, args[0].widget)
+                else:
+                    argStr = str(args)
+                _traceTkFile.write('CALLBACK> %d:%s%s%s\n' %
+                    (_recursionCounter, '  ' * _recursionCounter, name, argStr))
+                _traceTkFile.flush()
+	    return apply(self.func, args)
+	except SystemExit, msg:
+	    raise SystemExit, msg
+	except:
+	    _reporterror(self.func, args)
+
+_eventTypeToName = {
+    2 : 'KeyPress',         15 : 'VisibilityNotify',   28 : 'PropertyNotify',
+    3 : 'KeyRelease',       16 : 'CreateNotify',       29 : 'SelectionClear',
+    4 : 'ButtonPress',      17 : 'DestroyNotify',      30 : 'SelectionRequest',
+    5 : 'ButtonRelease',    18 : 'UnmapNotify',        31 : 'SelectionNotify',
+    6 : 'MotionNotify',     19 : 'MapNotify',          32 : 'ColormapNotify',
+    7 : 'EnterNotify',      20 : 'MapRequest',         33 : 'ClientMessage',
+    8 : 'LeaveNotify',      21 : 'ReparentNotify',     34 : 'MappingNotify',
+    9 : 'FocusIn',          22 : 'ConfigureNotify',    35 : 'VirtualEvents',
+    10 : 'FocusOut',        23 : 'ConfigureRequest',   36 : 'ActivateNotify',
+    11 : 'KeymapNotify',    24 : 'GravityNotify',      37 : 'DeactivateNotify',
+    12 : 'Expose',          25 : 'ResizeRequest',      38 : 'MouseWheelEvent',
+    13 : 'GraphicsExpose',  26 : 'CirculateNotify',
+    14 : 'NoExpose',        27 : 'CirculateRequest',
+}
+
+def _reporterror(func, args):
+    # Fetch current exception values.
+    exc_type, exc_value, exc_traceback = sys.exc_info()
+
+    # Give basic information about the callback exception.
+    if type(exc_type) == types.ClassType:
+	# Handle python 1.5 class exceptions.
+	exc_type = exc_type.__name__
+    msg = exc_type + ' Exception in Tk callback\n'
+    msg = msg + '  Function: %s (type: %s)\n' % (repr(func), type(func))
+    msg = msg + '  Args: %s\n' % str(args)
+
+    if type(args) == types.TupleType and len(args) > 0 and \
+	    hasattr(args[0], 'type'):
+        eventArg = 1
+    else:
+        eventArg = 0
+
+    # If the argument to the callback is an event, add the event type.
+    if eventArg:
+	eventNum = string.atoi(args[0].type)
+        if eventNum in _eventTypeToName.keys():
+            msg = msg + '  Event type: %s (type num: %d)\n' % \
+                    (_eventTypeToName[eventNum], eventNum)
+        else:
+            msg = msg + '  Unknown event type (type num: %d)\n' % eventNum
+
+    # Add the traceback.
+    msg = msg + 'Traceback (innermost last):\n'
+    for tr in traceback.extract_tb(exc_traceback):
+	msg = msg + '  File "%s", line %s, in %s\n' % (tr[0], tr[1], tr[2])
+	msg = msg + '    %s\n' % tr[3]
+    msg = msg + '%s: %s\n' % (exc_type, exc_value)
+
+    # If the argument to the callback is an event, add the event contents.
+    if eventArg:
+	msg = msg + '\n================================================\n'
+	msg = msg + '  Event contents:\n'
+	keys = args[0].__dict__.keys()
+	keys.sort()
+	for key in keys:
+	    msg = msg + '    %s: %s\n' % (key, args[0].__dict__[key])
+
+    clearbusycursor()
+    try:
+	displayerror(msg)
+    except:
+        pass
+
+class _ErrorWindow:
+    def __init__(self):
+
+	self._errorQueue = []
+	self._errorCount = 0
+	self._open = 0
+        self._firstShowing = 1
+
+	# Create the toplevel window
+	self._top = Tkinter.Toplevel()
+	self._top.protocol('WM_DELETE_WINDOW', self._hide)
+	self._top.title('Error in background function')
+	self._top.iconname('Background error')
+
+	# Create the text widget and scrollbar in a frame
+	upperframe = Tkinter.Frame(self._top)
+
+	scrollbar = Tkinter.Scrollbar(upperframe, orient='vertical')
+	scrollbar.pack(side = 'right', fill = 'y')
+
+	self._text = Tkinter.Text(upperframe, yscrollcommand=scrollbar.set)
+	self._text.pack(fill = 'both', expand = 1)
+	scrollbar.configure(command=self._text.yview)
+
+	# Create the buttons and label in a frame
+	lowerframe = Tkinter.Frame(self._top)
+
+	ignore = Tkinter.Button(lowerframe,
+	        text = 'Ignore remaining errors', command = self._hide)
+	ignore.pack(side='left')
+
+	self._nextError = Tkinter.Button(lowerframe,
+	        text = 'Show next error', command = self._next)
+	self._nextError.pack(side='left')
+
+	self._label = Tkinter.Label(lowerframe, relief='ridge')
+	self._label.pack(side='left', fill='x', expand=1)
+
+	# Pack the lower frame first so that it does not disappear
+	# when the window is resized.
+	lowerframe.pack(side = 'bottom', fill = 'x')
+	upperframe.pack(side = 'bottom', fill = 'both', expand = 1)
+
+    def showerror(self, text):
+	if self._open:
+	    self._errorQueue.append(text)
+	else:
+	    self._display(text)
+	    self._open = 1
+
+	# Display the error window in the same place it was before.
+	if self._top.state() == 'normal':
+	    # If update_idletasks is not called here, the window may
+	    # be placed partially off the screen.  Also, if it is not
+	    # called and many errors are generated quickly in
+	    # succession, the error window may not display errors
+	    # until the last one is generated and the interpreter
+	    # becomes idle.
+	    # XXX: remove this, since it causes omppython to go into an
+	    # infinite loop if an error occurs in an omp callback.
+	    # self._top.update_idletasks()
+
+            pass
+	else:
+	    if self._firstShowing:
+		geom = None
+	    else:
+                geometry = self._top.geometry()
+                index = string.find(geometry, '+')
+                if index >= 0:
+                    geom = geometry[index:]
+                else:
+                    geom = None
+            setgeometryanddeiconify(self._top, geom)
+
+        if self._firstShowing:
+            self._firstShowing = 0
+        else:
+            self._top.tkraise()
+
+        self._top.focus()
+
+	self._updateButtons()
+
+	# Release any grab, so that buttons in the error window work.
+        releasegrabs()
+
+    def _hide(self):
+	self._errorCount = self._errorCount + len(self._errorQueue)
+	self._errorQueue = []
+	self._top.withdraw()
+	self._open = 0
+
+    def _next(self):
+	# Display the next error in the queue. 
+
+	text = self._errorQueue[0]
+	del self._errorQueue[0]
+
+	self._display(text)
+	self._updateButtons()
+
+    def _display(self, text):
+	self._errorCount = self._errorCount + 1
+	text = 'Error: %d\n%s' % (self._errorCount, text)
+	self._text.delete('1.0', 'end')
+	self._text.insert('end', text)
+
+    def _updateButtons(self):
+	numQueued = len(self._errorQueue)
+	if numQueued > 0:
+	    self._label.configure(text='%d more errors' % numQueued)
+	    self._nextError.configure(state='normal')
+	else:
+	    self._label.configure(text='No more errors')
+	    self._nextError.configure(state='disabled')
+
+######################################################################
+### File: PmwDialog.py
+# Based on iwidgets2.2.0/dialog.itk and iwidgets2.2.0/dialogshell.itk code.
+
+# Convention:
+#   Each dialog window should have one of these as the rightmost button:
+#     Close         Close a window which only displays information.
+#     Cancel        Close a window which may be used to change the state of
+#                   the application.
+
+import sys
+import types
+import Tkinter
+
+
+# A Toplevel with a ButtonBox and child site.
+
+class Dialog(MegaToplevel):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('buttonbox_hull_borderwidth',   1,         None),
+	    ('buttonbox_hull_relief',        'raised',  None),
+	    ('buttonboxpos',                 's',       INITOPT),
+	    ('buttons',                      ('OK',),   self._buttons),
+	    ('command',                      None,      None),
+	    ('dialogchildsite_borderwidth',  1,         None),
+	    ('dialogchildsite_relief',       'raised',  None),
+	    ('defaultbutton',                None,      self._defaultButton),
+            ('master',                       'parent',  None),
+	    ('separatorwidth',               0,         INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaToplevel.__init__(self, parent)
+
+	# Create the components.
+
+	oldInterior = MegaToplevel.interior(self)
+
+	# Set up pack options according to the position of the button box.
+        pos = self['buttonboxpos']
+	if pos not in 'nsew':
+	    raise ValueError, \
+	        'bad buttonboxpos option "%s":  should be n, s, e, or w' \
+		    % pos
+
+	if pos in 'ns':
+	    orient = 'horizontal'
+	    fill = 'x'
+	    if pos == 'n':
+	        side = 'top'
+	    else:
+	        side = 'bottom'
+	else:
+	    orient = 'vertical'
+	    fill = 'y'
+	    if pos == 'w':
+	        side = 'left'
+	    else:
+	        side = 'right'
+
+	# Create the button box.
+	self._buttonBox = self.createcomponent('buttonbox',
+		(), None,
+		ButtonBox, (oldInterior,), orient = orient)
+	self._buttonBox.pack(side = side, fill = fill)
+
+	# Create the separating line.
+	width = self['separatorwidth']
+	if width > 0:
+	    self._separator = self.createcomponent('separator',
+		    (), None,
+		    Tkinter.Frame, (oldInterior,), relief = 'sunken',
+		    height = width, width = width, borderwidth = width / 2)
+	    self._separator.pack(side = side, fill = fill)
+	
+	# Create the child site.
+	self.__dialogChildSite = self.createcomponent('dialogchildsite',
+		(), None,
+		Tkinter.Frame, (oldInterior,))
+	self.__dialogChildSite.pack(side=side, fill='both', expand=1)
+
+	self.oldButtons = ()
+	self.oldDefault = None
+
+	self.bind('<Return>', self._invokeDefault)
+        self.userdeletefunc(self._doCommand)
+        self.usermodaldeletefunc(self._doCommand)
+	
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def interior(self):
+	return self.__dialogChildSite
+
+    def invoke(self, index = DEFAULT):
+	return self._buttonBox.invoke(index)
+
+    def _invokeDefault(self, event):
+	try:
+	    self._buttonBox.index(DEFAULT)
+	except ValueError:
+	    return
+	self._buttonBox.invoke()
+
+    def _doCommand(self, name = None):
+        if name is not None and self.active() and \
+                grabstacktopwindow() != self.component('hull'):
+            # This is a modal dialog but is not on the top of the grab
+            # stack (ie:  should not have the grab), so ignore this
+            # event.  This seems to be a bug in Tk and may occur in
+            # nested modal dialogs.
+            #
+            # An example is the PromptDialog demonstration.  To
+            # trigger the problem, start the demo, then move the mouse
+            # to the main window, hit <TAB> and then <TAB> again.  The
+            # highlight border of the "Show prompt dialog" button
+            # should now be displayed.  Now hit <SPACE>, <RETURN>,
+            # <RETURN> rapidly several times.  Eventually, hitting the
+            # return key invokes the password dialog "OK" button even
+            # though the confirm dialog is active (and therefore
+            # should have the keyboard focus).  Observed under Solaris
+            # 2.5.1, python 1.5.2 and Tk8.0.
+
+            # TODO:  Give focus to the window on top of the grabstack.
+            return
+
+	command = self['command']
+	if callable(command):
+	    return command(name)
+	else:
+	    if self.active():
+	        self.deactivate(name)
+	    else:
+	        self.withdraw()
+
+    def _buttons(self):
+	buttons = self['buttons']
+	if type(buttons) != types.TupleType and type(buttons) != types.ListType:
+	    raise ValueError, \
+	        'bad buttons option "%s": should be a tuple' % str(buttons)
+	if self.oldButtons == buttons:
+	  return
+
+	self.oldButtons = buttons
+
+	for index in range(self._buttonBox.numbuttons()):
+	    self._buttonBox.delete(0)
+	for name in buttons:
+	    self._buttonBox.add(name,
+		command=lambda self=self, name=name: self._doCommand(name))
+
+	if len(buttons) > 0:
+	    defaultbutton = self['defaultbutton']
+	    if defaultbutton is None:
+		self._buttonBox.setdefault(None)
+	    else:
+		try:
+		    self._buttonBox.index(defaultbutton)
+		except ValueError:
+		    pass
+		else:
+		    self._buttonBox.setdefault(defaultbutton)
+	self._buttonBox.alignbuttons()
+
+    def _defaultButton(self):
+	defaultbutton = self['defaultbutton']
+	if self.oldDefault == defaultbutton:
+	  return
+
+	self.oldDefault = defaultbutton
+
+	if len(self['buttons']) > 0:
+	    if defaultbutton is None:
+		self._buttonBox.setdefault(None)
+	    else:
+		try:
+		    self._buttonBox.index(defaultbutton)
+		except ValueError:
+		    pass
+		else:
+		    self._buttonBox.setdefault(defaultbutton)
+
+######################################################################
+### File: PmwTimeFuncs.py
+# Functions for dealing with dates and times.
+
+import re
+import string
+
+def timestringtoseconds(text, separator = ':'):
+  inputList = string.split(string.strip(text), separator)
+  if len(inputList) != 3:
+    raise ValueError, 'invalid value: ' + text
+
+  sign = 1
+  if len(inputList[0]) > 0 and inputList[0][0] in ('+', '-'):
+    if inputList[0][0] == '-':
+      sign = -1
+    inputList[0] = inputList[0][1:]
+
+  if re.search('[^0-9]', string.join(inputList, '')) is not None:
+    raise ValueError, 'invalid value: ' + text
+
+  hour = string.atoi(inputList[0])
+  minute = string.atoi(inputList[1])
+  second = string.atoi(inputList[2])
+
+  if minute >= 60 or second >= 60:
+    raise ValueError, 'invalid value: ' + text
+  return sign * (hour * 60 * 60 + minute * 60 + second)
+
+_year_pivot = 50
+_century = 2000
+
+def setyearpivot(pivot, century = None):
+    global _year_pivot
+    global _century
+    oldvalues = (_year_pivot, _century)
+    _year_pivot = pivot
+    if century is not None:
+	_century = century
+    return oldvalues
+
+def datestringtojdn(text, format = 'ymd', separator = '/'):
+  inputList = string.split(string.strip(text), separator)
+  if len(inputList) != 3:
+    raise ValueError, 'invalid value: ' + text
+
+  if re.search('[^0-9]', string.join(inputList, '')) is not None:
+    raise ValueError, 'invalid value: ' + text
+  formatList = list(format)
+  day = string.atoi(inputList[formatList.index('d')])
+  month = string.atoi(inputList[formatList.index('m')])
+  year = string.atoi(inputList[formatList.index('y')])
+
+  if _year_pivot is not None:
+    if year >= 0 and year < 100:
+      if year <= _year_pivot:
+	year = year + _century
+      else:
+	year = year + _century - 100
+
+  jdn = ymdtojdn(year, month, day)
+  if jdntoymd(jdn) != (year, month, day):
+    raise ValueError, 'invalid value: ' + text
+  return jdn
+
+def _cdiv(a, b):
+    # Return a / b as calculated by most C language implementations,
+    # assuming both a and b are integers.
+
+    if a * b > 0:
+	return a / b
+    else:
+	return -(abs(a) / abs(b))
+
+def ymdtojdn(year, month, day, julian = -1, papal = 1):
+
+    # set Julian flag if auto set
+    if julian < 0:
+	if papal:                          # Pope Gregory XIII's decree
+	    lastJulianDate = 15821004L     # last day to use Julian calendar
+	else:                              # British-American usage
+	    lastJulianDate = 17520902L     # last day to use Julian calendar
+
+	julian = ((year * 100L) + month) * 100 + day  <=  lastJulianDate
+
+    if year < 0:
+	# Adjust BC year
+	year = year + 1
+
+    if julian:
+	return 367L * year - _cdiv(7 * (year + 5001L + _cdiv((month - 9), 7)), 4) + \
+	    _cdiv(275 * month, 9) + day + 1729777L
+    else:
+	return (day - 32076L) + \
+	    _cdiv(1461L * (year + 4800L + _cdiv((month - 14), 12)), 4) + \
+	    _cdiv(367 * (month - 2 - _cdiv((month - 14), 12) * 12), 12) - \
+	    _cdiv((3 * _cdiv((year + 4900L + _cdiv((month - 14), 12)), 100)), 4) + \
+	    1            # correction by rdg
+
+def jdntoymd(jdn, julian = -1, papal = 1):
+
+    # set Julian flag if auto set
+    if julian < 0:
+	if papal:                          # Pope Gregory XIII's decree
+	    lastJulianJdn = 2299160L       # last jdn to use Julian calendar
+	else:                              # British-American usage
+	    lastJulianJdn = 2361221L       # last jdn to use Julian calendar
+
+	julian = (jdn <= lastJulianJdn);
+
+    x = jdn + 68569L
+    if julian:
+	x = x + 38
+	daysPer400Years = 146100L
+	fudgedDaysPer4000Years = 1461000L + 1
+    else:
+	daysPer400Years = 146097L
+	fudgedDaysPer4000Years = 1460970L + 31
+
+    z = _cdiv(4 * x, daysPer400Years)
+    x = x - _cdiv((daysPer400Years * z + 3), 4)
+    y = _cdiv(4000 * (x + 1), fudgedDaysPer4000Years)
+    x = x - _cdiv(1461 * y, 4) + 31
+    m = _cdiv(80 * x, 2447)
+    d = x - _cdiv(2447 * m, 80)
+    x = _cdiv(m, 11)
+    m = m + 2 - 12 * x
+    y = 100 * (z - 49) + y + x
+
+    # Convert from longs to integers.
+    yy = int(y)
+    mm = int(m)
+    dd = int(d)
+
+    if yy <= 0:
+	# Adjust BC years.
+	    yy = yy - 1
+
+    return (yy, mm, dd)
+
+def stringtoreal(text, separator = '.'):
+    if separator != '.':
+	if string.find(text, '.') >= 0:
+	    raise ValueError, 'invalid value: ' + text
+	index = string.find(text, separator)
+	if index >= 0:
+	    text = text[:index] + '.' + text[index + 1:]
+    return string.atof(text)
+
+######################################################################
+### File: PmwBalloon.py
+import os
+import string
+import Tkinter
+
+
+class Balloon(MegaToplevel):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	optiondefs = (
+            ('initwait',                 500,            None), # milliseconds
+            ('label_background',         'lightyellow',  None),
+            ('label_foreground',         'black',        None),
+            ('label_justify',            'left',         None),
+            ('master',                   'parent',       None),
+            ('relmouse',                 'none',         self._relmouse),
+            ('state',                    'both',         self._state),
+            ('statuscommand',            None,           None),
+            ('xoffset',                  20,             None), # pixels
+            ('yoffset',                  1,              None), # pixels
+	    ('hull_highlightthickness',  1,              None),
+	    ('hull_highlightbackground', 'black',        None),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaToplevel.__init__(self, parent)
+
+	self.withdraw()
+	self.overrideredirect(1)
+
+	# Create the components.
+	interior = self.interior()
+	self._label = self.createcomponent('label',
+		(), None,
+		Tkinter.Label, (interior,))
+	self._label.pack()
+
+        # The default hull configuration options give a black border
+        # around the balloon, but avoids a black 'flash' when the
+        # balloon is deiconified, before the text appears.
+        if not kw.has_key('hull_background'):
+            self.configure(hull_background = \
+                    str(self._label.cget('background')))
+
+	# Initialise instance variables.
+	self._timer = None
+
+        # The widget or item that is currently triggering the balloon. 
+        # It is None if the balloon is not being displayed.  It is a
+        # one-tuple if the balloon is being displayed in response to a
+        # widget binding (value is the widget).  It is a two-tuple if
+        # the balloon is being displayed in response to a canvas or
+        # text item binding (value is the widget and the item).
+        self._currentTrigger = None
+	
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self._timer is not None:
+	    self.after_cancel(self._timer)
+	    self._timer = None
+	MegaToplevel.destroy(self)
+
+    def bind(self, widget, balloonHelp, statusHelp = None):
+
+        # If a previous bind for this widget exists, remove it.
+        self.unbind(widget)
+
+	if balloonHelp is None and statusHelp is None:
+	    return
+
+	if statusHelp is None:
+	    statusHelp = balloonHelp
+	enterId = widget.bind('<Enter>', 
+		lambda event, self = self, w = widget,
+			sHelp = statusHelp, bHelp = balloonHelp:
+				self._enter(event, w, sHelp, bHelp, 0))
+
+        # Set Motion binding so that if the pointer remains at rest
+        # within the widget until the status line removes the help and
+        # then the pointer moves again, then redisplay the help in the
+        # status line.
+	# Note:  The Motion binding only works for basic widgets, and
+        # the hull of megawidgets but not for other megawidget components.
+	motionId = widget.bind('<Motion>', 
+		lambda event = None, self = self, statusHelp = statusHelp:
+			self.showstatus(statusHelp))
+
+	leaveId = widget.bind('<Leave>', self._leave)
+	buttonId = widget.bind('<ButtonPress>', self._buttonpress)
+
+        # Set Destroy binding so that the balloon can be withdrawn and
+        # the timer can be cancelled if the widget is destroyed.
+	destroyId = widget.bind('<Destroy>', self._destroy)
+
+        # Use the None item in the widget's private Pmw dictionary to
+        # store the widget's bind callbacks, for later clean up.
+        if not hasattr(widget, '_Pmw_BalloonBindIds'):
+            widget._Pmw_BalloonBindIds = {}
+        widget._Pmw_BalloonBindIds[None] = \
+                (enterId, motionId, leaveId, buttonId, destroyId)
+
+    def unbind(self, widget):
+        if hasattr(widget, '_Pmw_BalloonBindIds'):
+            if widget._Pmw_BalloonBindIds.has_key(None):
+                (enterId, motionId, leaveId, buttonId, destroyId) = \
+                        widget._Pmw_BalloonBindIds[None]
+                # Need to pass in old bindings, so that Tkinter can
+                # delete the commands.  Otherwise, memory is leaked.
+                widget.unbind('<Enter>', enterId)
+                widget.unbind('<Motion>', motionId)
+                widget.unbind('<Leave>', leaveId)
+                widget.unbind('<ButtonPress>', buttonId)
+                widget.unbind('<Destroy>', destroyId)
+                del widget._Pmw_BalloonBindIds[None]
+
+        if self._currentTrigger is not None and len(self._currentTrigger) == 1:
+            # The balloon is currently being displayed and the current
+            # trigger is a widget.
+            triggerWidget = self._currentTrigger[0]
+            if triggerWidget == widget:
+                if self._timer is not None:
+                    self.after_cancel(self._timer)
+                    self._timer = None
+                self.withdraw()
+                self.clearstatus()
+                self._currentTrigger = None
+
+    def tagbind(self, widget, tagOrItem, balloonHelp, statusHelp = None):
+
+        # If a previous bind for this widget's tagOrItem exists, remove it.
+        self.tagunbind(widget, tagOrItem)
+
+	if balloonHelp is None and statusHelp is None:
+	    return
+
+	if statusHelp is None:
+	    statusHelp = balloonHelp
+	enterId = widget.tag_bind(tagOrItem, '<Enter>', 
+		lambda event, self = self, w = widget,
+			sHelp = statusHelp, bHelp = balloonHelp:
+				self._enter(event, w, sHelp, bHelp, 1))
+	motionId = widget.tag_bind(tagOrItem, '<Motion>', 
+		lambda event = None, self = self, statusHelp = statusHelp:
+			self.showstatus(statusHelp))
+	leaveId = widget.tag_bind(tagOrItem, '<Leave>', self._leave)
+	buttonId = widget.tag_bind(tagOrItem, '<ButtonPress>', self._buttonpress)
+
+        # Use the tagOrItem item in the widget's private Pmw dictionary to
+        # store the tagOrItem's bind callbacks, for later clean up.
+        if not hasattr(widget, '_Pmw_BalloonBindIds'):
+            widget._Pmw_BalloonBindIds = {}
+        widget._Pmw_BalloonBindIds[tagOrItem] = \
+                (enterId, motionId, leaveId, buttonId)
+
+    def tagunbind(self, widget, tagOrItem):
+        if hasattr(widget, '_Pmw_BalloonBindIds'):
+            if widget._Pmw_BalloonBindIds.has_key(tagOrItem):
+                (enterId, motionId, leaveId, buttonId) = \
+                        widget._Pmw_BalloonBindIds[tagOrItem]
+                widget.tag_unbind(tagOrItem, '<Enter>', enterId)
+                widget.tag_unbind(tagOrItem, '<Motion>', motionId)
+                widget.tag_unbind(tagOrItem, '<Leave>', leaveId)
+                widget.tag_unbind(tagOrItem, '<ButtonPress>', buttonId)
+                del widget._Pmw_BalloonBindIds[tagOrItem]
+
+        if self._currentTrigger is None:
+            # The balloon is not currently being displayed.
+            return
+
+        if len(self._currentTrigger) == 1:
+            # The current trigger is a widget.
+            return
+
+        if len(self._currentTrigger) == 2:
+            # The current trigger is a canvas item.
+            (triggerWidget, triggerItem) = self._currentTrigger
+            if triggerWidget == widget and triggerItem == tagOrItem:
+                if self._timer is not None:
+                    self.after_cancel(self._timer)
+                    self._timer = None
+                self.withdraw()
+                self.clearstatus()
+                self._currentTrigger = None
+        else: # The current trigger is a text item.
+            (triggerWidget, x, y) = self._currentTrigger
+            if triggerWidget == widget:
+                currentPos = widget.index('@%d,%d' % (x, y))
+                currentTags = widget.tag_names(currentPos)
+                if tagOrItem in currentTags:
+                    if self._timer is not None:
+                        self.after_cancel(self._timer)
+                        self._timer = None
+                    self.withdraw()
+                    self.clearstatus()
+                    self._currentTrigger = None
+
+    def showstatus(self, statusHelp):
+	if self['state'] in ('status', 'both'):
+	    cmd = self['statuscommand']
+	    if callable(cmd):
+		cmd(statusHelp)
+
+    def clearstatus(self):
+        self.showstatus(None)
+
+    def _state(self):
+	if self['state'] not in ('both', 'balloon', 'status', 'none'):
+	    raise ValueError, 'bad state option ' + repr(self['state']) + \
+		': should be one of \'both\', \'balloon\', ' + \
+		'\'status\' or \'none\''
+
+    def _relmouse(self):
+	if self['relmouse'] not in ('both', 'x', 'y', 'none'):
+	    raise ValueError, 'bad relmouse option ' + repr(self['relmouse'])+ \
+		': should be one of \'both\', \'x\', ' + '\'y\' or \'none\''
+
+    def _enter(self, event, widget, statusHelp, balloonHelp, isItem):
+
+        # Do not display balloon if mouse button is pressed.  This
+        # will only occur if the button was pressed inside a widget,
+        # then the mouse moved out of and then back into the widget,
+        # with the button still held down.  The number 0x1f00 is the
+        # button mask for the 5 possible buttons in X.
+        buttonPressed = (event.state & 0x1f00) != 0
+
+	if not buttonPressed and balloonHelp is not None and \
+                self['state'] in ('balloon', 'both'):
+	    if self._timer is not None:
+		self.after_cancel(self._timer)
+		self._timer = None
+
+	    self._timer = self.after(self['initwait'], 
+		    lambda self = self, widget = widget, help = balloonHelp,
+			    isItem = isItem:
+			    self._showBalloon(widget, help, isItem))
+
+        if isItem:
+            if hasattr(widget, 'canvasx'):
+		# The widget is a canvas.
+                item = widget.find_withtag('current')
+                if len(item) > 0:
+                    item = item[0]
+                else:
+                    item = None
+                self._currentTrigger = (widget, item)
+            else:
+		# The widget is a text widget.
+                self._currentTrigger = (widget, event.x, event.y)
+        else:
+            self._currentTrigger = (widget,)
+
+	self.showstatus(statusHelp)
+
+    def _leave(self, event):
+	if self._timer is not None:
+	    self.after_cancel(self._timer)
+	    self._timer = None
+	self.withdraw()
+	self.clearstatus()
+        self._currentTrigger = None
+
+    def _destroy(self, event):
+
+        # Only withdraw the balloon and cancel the timer if the widget
+        # being destroyed is the widget that triggered the balloon. 
+        # Note that in a Tkinter Destroy event, the widget field is a
+        # string and not a widget as usual.
+
+        if self._currentTrigger is None:
+            # The balloon is not currently being displayed
+            return
+
+        if len(self._currentTrigger) == 1:
+            # The current trigger is a widget (not an item)
+            triggerWidget = self._currentTrigger[0]
+            if str(triggerWidget) == event.widget:
+                if self._timer is not None:
+                    self.after_cancel(self._timer)
+                    self._timer = None
+                self.withdraw()
+                self.clearstatus()
+                self._currentTrigger = None
+
+    def _buttonpress(self, event):
+	if self._timer is not None:
+	    self.after_cancel(self._timer)
+	    self._timer = None
+	self.withdraw()
+        self._currentTrigger = None
+
+    def _showBalloon(self, widget, balloonHelp, isItem):
+
+	self._label.configure(text = balloonHelp)
+
+        # First, display the balloon offscreen to get dimensions.
+        screenWidth = self.winfo_screenwidth()
+        screenHeight = self.winfo_screenheight()
+        self.geometry('+%d+0' % (screenWidth + 1))
+        self.update_idletasks()
+
+	if isItem:
+            # Get the bounding box of the current item.
+            bbox = widget.bbox('current')
+            if bbox is None:
+                # The item that triggered the balloon has disappeared,
+                # perhaps by a user's timer event that occured between
+                # the <Enter> event and the 'initwait' timer calling
+                # this method.
+                return
+
+	    # The widget is either a text or canvas.  The meaning of
+	    # the values returned by the bbox method is different for
+	    # each, so use the existence of the 'canvasx' method to
+	    # distinguish between them.
+	    if hasattr(widget, 'canvasx'):
+		# The widget is a canvas.  Place balloon under canvas
+                # item.  The positions returned by bbox are relative
+                # to the entire canvas, not just the visible part, so
+                # need to convert to window coordinates.
+                leftrel = bbox[0] - widget.canvasx(0)
+                toprel = bbox[1] - widget.canvasy(0)
+                bottomrel = bbox[3] - widget.canvasy(0)
+	    else:
+		# The widget is a text widget.  Place balloon under
+                # the character closest to the mouse.  The positions
+                # returned by bbox are relative to the text widget
+                # window (ie the visible part of the text only).
+		leftrel = bbox[0]
+                toprel = bbox[1]
+		bottomrel = bbox[1] + bbox[3]
+	else:
+	    leftrel = 0
+            toprel = 0
+	    bottomrel = widget.winfo_height()
+
+        xpointer, ypointer = widget.winfo_pointerxy()   # -1 if off screen
+
+        if xpointer >= 0 and self['relmouse'] in ('both', 'x'):
+            x = xpointer
+        else:
+            x = leftrel + widget.winfo_rootx()
+        x = x + self['xoffset']
+
+        if ypointer >= 0 and self['relmouse'] in ('both', 'y'):
+            y = ypointer
+        else:
+            y = bottomrel + widget.winfo_rooty()
+        y = y + self['yoffset']
+
+        edges = (string.atoi(str(self.cget('hull_highlightthickness'))) +
+            string.atoi(str(self.cget('hull_borderwidth')))) * 2
+        if x + self._label.winfo_reqwidth() + edges > screenWidth:
+            x = screenWidth - self._label.winfo_reqwidth() - edges
+
+        if y + self._label.winfo_reqheight() + edges > screenHeight:
+            if ypointer >= 0 and self['relmouse'] in ('both', 'y'):
+                y = ypointer
+            else:
+                y = toprel + widget.winfo_rooty()
+            y = y - self._label.winfo_reqheight() - self['yoffset'] - edges
+
+        setgeometryanddeiconify(self, '+%d+%d' % (x, y))
+
+######################################################################
+### File: PmwButtonBox.py
+# Based on iwidgets2.2.0/buttonbox.itk code.
+
+import types
+import Tkinter
+
+
+class ButtonBox(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('labelmargin',       0,              INITOPT),
+	    ('labelpos',          None,           INITOPT),
+	    ('orient',            'horizontal',   INITOPT),
+	    ('padx',              3,              INITOPT),
+	    ('pady',              3,              INITOPT),
+	)
+	self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',))
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	if self['labelpos'] is None:
+	    self._buttonBoxFrame = self._hull
+	    columnOrRow = 0
+	else:
+	    self._buttonBoxFrame = self.createcomponent('frame',
+		    (), None,
+		    Tkinter.Frame, (interior,))
+	    self._buttonBoxFrame.grid(column=2, row=2, sticky='nsew')
+	    columnOrRow = 2
+
+	    self.createlabel(interior)
+
+	orient = self['orient']
+	if orient == 'horizontal':
+	    interior.grid_columnconfigure(columnOrRow, weight = 1)
+	elif orient == 'vertical':
+	    interior.grid_rowconfigure(columnOrRow, weight = 1)
+	else:
+	    raise ValueError, 'bad orient option ' + repr(orient) + \
+		': must be either \'horizontal\' or \'vertical\''
+
+	# Initialise instance variables.
+
+	# List of tuples describing the buttons:
+	#   - name
+	#   - button widget
+	self._buttonList = []
+
+	# The index of the default button.
+	self._defaultButton = None
+
+	self._timerId = None
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self._timerId:
+	    self.after_cancel(self._timerId)
+	    self._timerId = None
+	MegaWidget.destroy(self)
+
+    def numbuttons(self):
+        return len(self._buttonList)
+
+    def index(self, index, forInsert = 0):
+	listLength = len(self._buttonList)
+	if type(index) == types.IntType:
+	    if forInsert and index <= listLength:
+		return index
+	    elif not forInsert and index < listLength:
+		return index
+	    else:
+		raise ValueError, 'index "%s" is out of range' % index
+	elif index is END:
+	    if forInsert:
+		return listLength
+	    elif listLength > 0:
+		return listLength - 1
+	    else:
+		raise ValueError, 'ButtonBox has no buttons'
+	elif index is DEFAULT:
+	    if self._defaultButton is not None:
+		return self._defaultButton
+	    raise ValueError, 'ButtonBox has no default'
+	else:
+            names = map(lambda t: t[0], self._buttonList)
+            if index in names:
+                return names.index(index)
+	    validValues = 'a name, a number, END or DEFAULT'
+	    raise ValueError, \
+		'bad index "%s": must be %s' % (index, validValues)
+
+    def insert(self, componentName, beforeComponent = 0, **kw):
+	if componentName in self.components():
+	    raise ValueError, 'button "%s" already exists' % componentName
+	if not kw.has_key('text'):
+	    kw['text'] = componentName
+        kw['default'] = 'normal'
+	button = apply(self.createcomponent, (componentName,
+		(), 'Button',
+		Tkinter.Button, (self._buttonBoxFrame,)), kw)
+
+	index = self.index(beforeComponent, 1)
+	horizontal = self['orient'] == 'horizontal'
+	numButtons = len(self._buttonList)
+
+	# Shift buttons up one position.
+	for i in range(numButtons - 1, index - 1, -1):
+	    widget = self._buttonList[i][1]
+	    pos = i * 2 + 3
+	    if horizontal:
+		widget.grid(column = pos, row = 0)
+	    else:
+		widget.grid(column = 0, row = pos)
+
+	# Display the new button.
+	if horizontal:
+	    button.grid(column = index * 2 + 1, row = 0, sticky = 'ew',
+		    padx = self['padx'], pady = self['pady'])
+	    self._buttonBoxFrame.grid_columnconfigure(
+		    numButtons * 2 + 2, weight = 1)
+	else:
+	    button.grid(column = 0, row = index * 2 + 1, sticky = 'ew',
+		    padx = self['padx'], pady = self['pady'])
+	    self._buttonBoxFrame.grid_rowconfigure(
+		    numButtons * 2 + 2, weight = 1)
+	self._buttonList.insert(index, (componentName, button))
+
+	return button
+
+    def add(self, componentName, **kw):
+        return apply(self.insert, (componentName, len(self._buttonList)), kw)
+
+    def delete(self, index):
+        index = self.index(index)
+	(name, widget) = self._buttonList[index]
+	widget.grid_forget()
+	self.destroycomponent(name)
+
+	numButtons = len(self._buttonList)
+
+	# Shift buttons down one position.
+	horizontal = self['orient'] == 'horizontal'
+	for i in range(index + 1, numButtons):
+	    widget = self._buttonList[i][1]
+	    pos = i * 2 - 1
+	    if horizontal:
+		widget.grid(column = pos, row = 0)
+	    else:
+		widget.grid(column = 0, row = pos)
+
+	if horizontal:
+	    self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 - 1,
+		    minsize = 0)
+	    self._buttonBoxFrame.grid_columnconfigure(numButtons * 2, weight = 0)
+	else:
+	    self._buttonBoxFrame.grid_rowconfigure(numButtons * 2, weight = 0)
+	del self._buttonList[index]
+
+    def setdefault(self, index):
+	# Turn off the default ring around the current default button.
+	if self._defaultButton is not None:
+	    button = self._buttonList[self._defaultButton][1]
+	    button.configure(default = 'normal')
+	    self._defaultButton = None
+
+	# Turn on the default ring around the new default button.
+	if index is not None:
+	    index = self.index(index)
+	    self._defaultButton = index
+	    button = self._buttonList[index][1]
+	    button.configure(default = 'active')
+
+    def invoke(self, index = DEFAULT, noFlash = 0):
+	# Invoke the callback associated with the *index* button.  If
+	# *noFlash* is not set, flash the button to indicate to the
+	# user that something happened.
+
+	button = self._buttonList[self.index(index)][1]
+	if not noFlash:
+	    state = button.cget('state')
+	    relief = button.cget('relief')
+	    button.configure(state = 'active', relief = 'sunken')
+	    self.update_idletasks()
+	    self.after(100)
+	    button.configure(state = state, relief = relief)
+	return button.invoke()
+
+    def button(self, buttonIndex):
+	return self._buttonList[self.index(buttonIndex)][1]
+
+    def alignbuttons(self, when = 'later'):
+	if when == 'later':
+	    if not self._timerId:
+		self._timerId = self.after_idle(self.alignbuttons, 'now')
+	    return
+	self.update_idletasks()
+	self._timerId = None
+
+        # Determine the width of the maximum length button.
+        max = 0
+        horizontal = (self['orient'] == 'horizontal')
+        for index in range(len(self._buttonList)):
+            gridIndex = index * 2 + 1
+            if horizontal:
+                width = self._buttonBoxFrame.grid_bbox(gridIndex, 0)[2]
+            else:
+                width = self._buttonBoxFrame.grid_bbox(0, gridIndex)[2]
+            if width > max:
+                max = width
+
+        # Set the width of all the buttons to be the same.
+	if horizontal:
+	    for index in range(len(self._buttonList)):
+		self._buttonBoxFrame.grid_columnconfigure(index * 2 + 1,
+			minsize = max)
+	else:
+	    self._buttonBoxFrame.grid_columnconfigure(0, minsize = max)
+
+######################################################################
+### File: PmwEntryField.py
+# Based on iwidgets2.2.0/entryfield.itk code.
+
+import re
+import string
+import types
+import Tkinter
+
+
+# Possible return values of validation functions.
+OK = 1
+ERROR = 0
+PARTIAL = -1
+
+class EntryField(MegaWidget):
+    _classBindingsDefinedFor = 0
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('command',           None,        None),
+	    ('errorbackground',   'pink',      None),
+	    ('invalidcommand',    self.bell,   None),
+	    ('labelmargin',       0,           INITOPT),
+	    ('labelpos',          None,        INITOPT),
+	    ('modifiedcommand',   None,        None),
+	    ('sticky',            'ew',        INITOPT),
+	    ('validate',          None,        self._validate),
+	    ('extravalidators',   {},          None),
+	    ('value',             '',          INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	self._entryFieldEntry = self.createcomponent('entry',
+		(), None,
+		Tkinter.Entry, (interior,))
+	self._entryFieldEntry.grid(column=2, row=2, sticky=self['sticky'])
+	if self['value'] != '':
+	    self.__setEntry(self['value'])
+	interior.grid_columnconfigure(2, weight=1)
+	interior.grid_rowconfigure(2, weight=1)
+
+	self.createlabel(interior)
+
+	# Initialise instance variables.
+
+	self.normalBackground = None
+        self._previousText = None
+
+	# Initialise instance.
+
+	_registerEntryField(self._entryFieldEntry, self)
+
+        # Establish the special class bindings if not already done.
+        # Also create bindings if the Tkinter default interpreter has
+        # changed.  Use Tkinter._default_root to create class
+        # bindings, so that a reference to root is created by
+        # bind_class rather than a reference to self, which would
+        # prevent object cleanup.
+        if EntryField._classBindingsDefinedFor != Tkinter._default_root:
+	    tagList = self._entryFieldEntry.bindtags()
+            root  = Tkinter._default_root
+	    	    
+	    allSequences = {}
+	    for tag in tagList:
+
+                sequences = root.bind_class(tag)
+                if type(sequences) is types.StringType:
+                    # In old versions of Tkinter, bind_class returns a string
+                    sequences = root.tk.splitlist(sequences)
+
+		for sequence in sequences:
+		    allSequences[sequence] = None
+	    for sequence in allSequences.keys():
+		root.bind_class('EntryFieldPre', sequence, _preProcess)
+		root.bind_class('EntryFieldPost', sequence, _postProcess)
+
+	    EntryField._classBindingsDefinedFor = root
+
+	self._entryFieldEntry.bindtags(('EntryFieldPre',) +
+		self._entryFieldEntry.bindtags() + ('EntryFieldPost',))
+	self._entryFieldEntry.bind('<Return>', self._executeCommand)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	_deregisterEntryField(self._entryFieldEntry)
+        MegaWidget.destroy(self)
+
+    def _getValidatorFunc(self, validator, index):
+	# Search the extra and standard validator lists for the
+	# given 'validator'.  If 'validator' is an alias, then
+	# continue the search using the alias.  Make sure that
+	# self-referencial aliases do not cause infinite loops.
+
+	extraValidators = self['extravalidators']
+	traversedValidators = []
+
+	while 1:
+	    traversedValidators.append(validator)
+	    if extraValidators.has_key(validator):
+		validator = extraValidators[validator][index]
+	    elif _standardValidators.has_key(validator):
+		validator = _standardValidators[validator][index]
+	    else:
+		return validator
+	    if validator in traversedValidators:
+		return validator
+
+    def _validate(self):
+	dict = {
+	    'validator' : None,
+	    'min' : None,
+	    'max' : None,
+	    'minstrict' : 1,
+	    'maxstrict' : 1,
+	}
+	opt = self['validate']
+	if type(opt) is types.DictionaryType:
+	    dict.update(opt)
+	else:
+	    dict['validator'] = opt
+
+	# Look up validator maps and replace 'validator' field with
+	# the corresponding function.
+	validator = dict['validator']
+	valFunction = self._getValidatorFunc(validator, 0)
+	self._checkValidateFunction(valFunction, 'validate', validator)
+	dict['validator'] = valFunction
+
+	# Look up validator maps and replace 'stringtovalue' field
+	# with the corresponding function.
+	if dict.has_key('stringtovalue'):
+	    stringtovalue = dict['stringtovalue'] 
+	    strFunction = self._getValidatorFunc(stringtovalue, 1)
+	    self._checkValidateFunction(
+		    strFunction, 'stringtovalue', stringtovalue)
+	else:
+	    strFunction = self._getValidatorFunc(validator, 1)
+	    if strFunction == validator:
+		strFunction = len
+	dict['stringtovalue'] = strFunction
+
+	self._validationInfo = dict
+	args = dict.copy()
+	del args['validator']
+	del args['min']
+	del args['max']
+	del args['minstrict']
+	del args['maxstrict']
+	del args['stringtovalue']
+	self._validationArgs = args
+        self._previousText = None
+
+	if type(dict['min']) == types.StringType and strFunction is not None:
+	    dict['min'] = apply(strFunction, (dict['min'],), args)
+	if type(dict['max']) == types.StringType and strFunction is not None:
+	    dict['max'] = apply(strFunction, (dict['max'],), args)
+
+	self._checkValidity()
+
+    def _checkValidateFunction(self, function, option, validator):
+	# Raise an error if 'function' is not a function or None.
+
+	if function is not None and not callable(function):
+	    extraValidators = self['extravalidators']
+	    extra = extraValidators.keys()
+	    extra.sort()
+	    extra = tuple(extra)
+	    standard = _standardValidators.keys()
+	    standard.sort()
+	    standard = tuple(standard)
+	    msg = 'bad %s value "%s":  must be a function or one of ' \
+		'the standard validators %s or extra validators %s'
+	    raise ValueError, msg % (option, validator, standard, extra)
+
+    def _executeCommand(self, event = None):
+	cmd = self['command']
+	if callable(cmd):
+            if event is None:
+                # Return result of command for invoke() method.
+                return cmd()
+            else:
+                cmd()
+	    
+    def _preProcess(self):
+
+        self._previousText = self._entryFieldEntry.get()
+        self._previousICursor = self._entryFieldEntry.index('insert')
+        self._previousXview = self._entryFieldEntry.index('@0')
+	if self._entryFieldEntry.selection_present():
+	    self._previousSel= (self._entryFieldEntry.index('sel.first'),
+		self._entryFieldEntry.index('sel.last'))
+	else:
+	    self._previousSel = None
+
+    def _postProcess(self):
+
+	# No need to check if text has not changed.
+	previousText = self._previousText
+	if previousText == self._entryFieldEntry.get():
+	    return self.valid()
+
+	valid = self._checkValidity()
+        if self.hulldestroyed():
+            # The invalidcommand called by _checkValidity() destroyed us.
+            return valid
+
+	cmd = self['modifiedcommand']
+	if callable(cmd) and previousText != self._entryFieldEntry.get():
+	    cmd()
+	return valid
+	    
+    def checkentry(self):
+	# If there is a variable specified by the entry_textvariable
+	# option, checkentry() should be called after the set() method
+	# of the variable is called.
+
+	self._previousText = None
+	return self._postProcess()
+
+    def _getValidity(self):
+	text = self._entryFieldEntry.get()
+	dict = self._validationInfo
+	args = self._validationArgs
+
+	if dict['validator'] is not None:
+	    status = apply(dict['validator'], (text,), args)
+	    if status != OK:
+		return status
+
+	# Check for out of (min, max) range.
+	if dict['stringtovalue'] is not None:
+	    min = dict['min']
+	    max = dict['max']
+	    if min is None and max is None:
+		return OK
+	    val = apply(dict['stringtovalue'], (text,), args)
+	    if min is not None and val < min:
+		if dict['minstrict']:
+		    return ERROR
+		else:
+		    return PARTIAL
+	    if max is not None and val > max:
+		if dict['maxstrict']:
+		    return ERROR
+		else:
+		    return PARTIAL
+	return OK
+
+    def _checkValidity(self):
+	valid = self._getValidity()
+	oldValidity = valid
+
+	if valid == ERROR:
+	    # The entry is invalid.
+	    cmd = self['invalidcommand']
+	    if callable(cmd):
+		cmd()
+            if self.hulldestroyed():
+                # The invalidcommand destroyed us.
+                return oldValidity
+
+	    # Restore the entry to its previous value.
+	    if self._previousText is not None:
+		self.__setEntry(self._previousText)
+		self._entryFieldEntry.icursor(self._previousICursor)
+		self._entryFieldEntry.xview(self._previousXview)
+		if self._previousSel is not None:
+		    self._entryFieldEntry.selection_range(self._previousSel[0],
+			self._previousSel[1])
+
+		# Check if the saved text is valid as well.
+		valid = self._getValidity()
+
+	self._valid = valid
+
+        if self.hulldestroyed():
+            # The validator or stringtovalue commands called by
+            # _checkValidity() destroyed us.
+            return oldValidity
+
+	if valid == OK:
+	    if self.normalBackground is not None:
+		self._entryFieldEntry.configure(
+			background = self.normalBackground)
+		self.normalBackground = None
+	else:
+	    if self.normalBackground is None:
+		self.normalBackground = self._entryFieldEntry.cget('background')
+		self._entryFieldEntry.configure(
+			background = self['errorbackground'])
+
+        return oldValidity
+
+    def invoke(self):
+	return self._executeCommand()
+
+    def valid(self):
+        return self._valid == OK
+
+    def clear(self):
+        self.setentry('')
+
+    def __setEntry(self, text):
+	oldState = str(self._entryFieldEntry.cget('state'))
+	if oldState != 'normal':
+	    self._entryFieldEntry.configure(state='normal')
+	self._entryFieldEntry.delete(0, 'end')
+	self._entryFieldEntry.insert(0, text)
+	if oldState != 'normal':
+	    self._entryFieldEntry.configure(state=oldState)
+
+    def setentry(self, text):
+	self._preProcess()
+        self.__setEntry(text)
+	return self._postProcess()
+
+    def getvalue(self):
+        return self._entryFieldEntry.get()
+
+    def setvalue(self, text):
+        return self.setentry(text)
+
+forwardmethods(EntryField, Tkinter.Entry, '_entryFieldEntry')
+
+# ======================================================================
+
+
+# Entry field validation functions
+
+_numericregex = re.compile('^[0-9]*$')
+_alphabeticregex = re.compile('^[a-z]*$', re.IGNORECASE)
+_alphanumericregex = re.compile('^[0-9a-z]*$', re.IGNORECASE)
+
+def numericvalidator(text):
+    if text == '':
+        return PARTIAL
+    else:
+	if _numericregex.match(text) is None:
+	    return ERROR
+	else:
+	    return OK
+    
+def integervalidator(text):
+    if text in ('', '-', '+'):
+        return PARTIAL
+    try:
+	string.atol(text)
+	return OK
+    except ValueError:
+	return ERROR
+    
+def alphabeticvalidator(text):
+    if _alphabeticregex.match(text) is None:
+	return ERROR
+    else:
+	return OK
+    
+def alphanumericvalidator(text):
+    if _alphanumericregex.match(text) is None:
+	return ERROR
+    else:
+	return OK
+    
+def hexadecimalvalidator(text):
+    if text in ('', '0x', '0X', '+', '+0x', '+0X', '-', '-0x', '-0X'):
+        return PARTIAL
+    try:
+	string.atol(text, 16)
+	return OK
+    except ValueError:
+	return ERROR
+    
+def realvalidator(text, separator = '.'):
+    if separator != '.':
+	if string.find(text, '.') >= 0:
+	    return ERROR
+	index = string.find(text, separator)
+	if index >= 0:
+	    text = text[:index] + '.' + text[index + 1:]
+    try:
+	string.atof(text)
+	return OK
+    except ValueError:
+	# Check if the string could be made valid by appending a digit
+	# eg ('-', '+', '.', '-.', '+.', '1.23e', '1E-').
+	if len(text) == 0:
+	    return PARTIAL
+	if text[-1] in string.digits:
+	    return ERROR
+	try:
+	    string.atof(text + '0')
+	    return PARTIAL
+	except ValueError:
+	    return ERROR
+    
+def timevalidator(text, separator = ':'):
+    try:
+	timestringtoseconds(text, separator)
+	return OK
+    except ValueError:
+	if len(text) > 0 and text[0] in ('+', '-'):
+	    text = text[1:]
+	if re.search('[^0-9' + separator + ']', text) is not None:
+	    return ERROR
+	return PARTIAL
+
+def datevalidator(text, format = 'ymd', separator = '/'):
+    try:
+	datestringtojdn(text, format, separator)
+	return OK
+    except ValueError:
+	if re.search('[^0-9' + separator + ']', text) is not None:
+	    return ERROR
+	return PARTIAL
+
+_standardValidators = {
+    'numeric'      : (numericvalidator,      string.atol),
+    'integer'      : (integervalidator,      string.atol),
+    'hexadecimal'  : (hexadecimalvalidator,  lambda s: string.atol(s, 16)),
+    'real'         : (realvalidator,         stringtoreal),
+    'alphabetic'   : (alphabeticvalidator,   len),
+    'alphanumeric' : (alphanumericvalidator, len),
+    'time'         : (timevalidator,         timestringtoseconds),
+    'date'         : (datevalidator,         datestringtojdn),
+}
+
+_entryCache = {}
+
+def _registerEntryField(entry, entryField):
+    # Register an EntryField widget for an Entry widget
+
+    _entryCache[entry] = entryField
+
+def _deregisterEntryField(entry):
+    # Deregister an Entry widget
+    del _entryCache[entry]
+
+def _preProcess(event):
+    # Forward preprocess events for an Entry to it's EntryField
+
+    _entryCache[event.widget]._preProcess()
+
+def _postProcess(event):
+    # Forward postprocess events for an Entry to it's EntryField
+
+    # The function specified by the 'command' option may have destroyed
+    # the megawidget in a binding earlier in bindtags, so need to check.
+    if _entryCache.has_key(event.widget):
+        _entryCache[event.widget]._postProcess()
+
+######################################################################
+### File: PmwGroup.py
+import string
+import Tkinter
+
+
+def aligngrouptags(groups):
+    # Adjust the y position of the tags in /groups/ so that they all
+    # have the height of the highest tag.
+
+    maxTagHeight = 0
+    for group in groups:
+	if group._tag is None:
+	    height = (string.atoi(str(group._ring.cget('borderwidth'))) +
+		    string.atoi(str(group._ring.cget('highlightthickness'))))
+	else:
+	    height = group._tag.winfo_reqheight()
+	if maxTagHeight < height:
+	    maxTagHeight = height
+
+    for group in groups:
+	ringBorder = (string.atoi(str(group._ring.cget('borderwidth'))) +
+		string.atoi(str(group._ring.cget('highlightthickness'))))
+	topBorder = maxTagHeight / 2 - ringBorder / 2
+	group._hull.grid_rowconfigure(0, minsize = topBorder)
+	group._ring.grid_rowconfigure(0,
+		minsize = maxTagHeight - topBorder - ringBorder)
+	if group._tag is not None:
+	    group._tag.place(y = maxTagHeight / 2)
+
+class Group( MegaWidget ):
+    def __init__(self, parent = None, **kw):
+
+        # Define the megawidget options.
+	
+        optiondefs = (
+	    ('collapsedsize',    6,         INITOPT),
+	    ('ring_borderwidth', 2,         None),
+	    ('ring_relief',      'groove',  None),
+	    ('tagindent',        10,        INITOPT),
+        )
+        self.defineoptions(kw, optiondefs)
+
+        # Initialise the base class (after defining the options).
+        MegaWidget.__init__(self, parent)
+
+        # Create the components.
+        interior = MegaWidget.interior(self)
+
+	self._ring = self.createcomponent(
+	    'ring', 
+	    (), None,
+	    Tkinter.Frame, (interior,), 
+	    )
+
+	self._groupChildSite = self.createcomponent(
+	    'groupchildsite',
+	    (), None,
+	    Tkinter.Frame, (self._ring,)
+	    )
+
+        self._tag = self.createcomponent(
+	    'tag',
+	    (), None,
+	    Tkinter.Label, (interior,),
+	    )
+
+	ringBorder = (string.atoi(str(self._ring.cget('borderwidth'))) +
+		string.atoi(str(self._ring.cget('highlightthickness'))))
+	if self._tag is None:
+	    tagHeight = ringBorder
+	else:
+	    tagHeight = self._tag.winfo_reqheight()
+	    self._tag.place(
+		    x = ringBorder + self['tagindent'],
+		    y = tagHeight / 2,
+		    anchor = 'w')
+
+	topBorder = tagHeight / 2 - ringBorder / 2
+	self._ring.grid(column = 0, row = 1, sticky = 'nsew')
+	interior.grid_columnconfigure(0, weight = 1)
+	interior.grid_rowconfigure(1, weight = 1)
+	interior.grid_rowconfigure(0, minsize = topBorder)
+
+	self._groupChildSite.grid(column = 0, row = 1, sticky = 'nsew')
+	self._ring.grid_columnconfigure(0, weight = 1)
+	self._ring.grid_rowconfigure(1, weight = 1)
+	self._ring.grid_rowconfigure(0,
+		minsize = tagHeight - topBorder - ringBorder)
+
+        self.showing = 1
+
+        # Check keywords and initialise options.
+        self.initialiseoptions()
+
+    def toggle(self):
+        if self.showing:
+            self.collapse()
+        else:
+            self.expand()
+        self.showing = not self.showing
+
+    def expand(self):
+        self._groupChildSite.grid(column = 0, row = 1, sticky = 'nsew')
+
+    def collapse(self):
+        self._groupChildSite.grid_forget()
+	if self._tag is None:
+	    tagHeight = 0
+	else:
+            tagHeight = self._tag.winfo_reqheight()
+        self._ring.configure(height=(tagHeight / 2) + self['collapsedsize'])
+
+    def interior(self):
+        return self._groupChildSite
+
+######################################################################
+### File: PmwLabeledWidget.py
+import Tkinter
+
+
+class LabeledWidget(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('labelmargin',            0,      INITOPT),
+	    ('labelpos',               None,   INITOPT),
+	    ('sticky',                 'nsew', INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = MegaWidget.interior(self)
+	self._labelChildSite = self.createcomponent('labelchildsite',
+		(), None,
+		Tkinter.Frame, (interior,))
+	self._labelChildSite.grid(column=2, row=2, sticky=self['sticky'])
+	interior.grid_columnconfigure(2, weight=1)
+	interior.grid_rowconfigure(2, weight=1)
+
+	self.createlabel(interior)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def interior(self):
+	return self._labelChildSite
+
+######################################################################
+### File: PmwMainMenuBar.py
+# Main menubar
+
+import string
+import types
+import Tkinter
+
+
+class MainMenuBar(MegaArchetype):
+
+    def __init__(self, parent = None, **kw):
+
+        # Define the megawidget options.
+        
+        optiondefs = (
+            ('balloon',      None,       None),
+            ('hotkeys',      1,          INITOPT),
+            ('hull_tearoff', 0,          None),
+        )
+        self.defineoptions(kw, optiondefs, dynamicGroups = ('Menu',))
+
+        # Initialise the base class (after defining the options).
+        MegaArchetype.__init__(self, parent, Tkinter.Menu)
+
+        self._menuInfo = {}
+        self._menuInfo[None] = (None, [])
+        # Map from a menu name to a tuple of information about the menu.
+        # The first item in the tuple is the name of the parent menu (for
+        # toplevel menus this is None). The second item in the tuple is
+        # a list of status help messages for each item in the menu.
+        # The key for the information for the main menubar is None.
+
+        self._menu = self.interior()
+        self._menu.bind('<Leave>', self._resetHelpmessage)
+        self._menu.bind('<Motion>',
+            lambda event=None, self=self: self._menuHelp(event, None))
+
+        # Check keywords and initialise options.
+        self.initialiseoptions()
+
+    def deletemenuitems(self, menuName, start, end = None):
+        self.component(menuName).delete(start, end)
+        if end is None:
+            del self._menuInfo[menuName][1][start]
+        else:
+            self._menuInfo[menuName][1][start:end+1] = []
+
+    def deletemenu(self, menuName):
+        """Delete should be called for cascaded menus before main menus.
+        """
+
+        parentName = self._menuInfo[menuName][0]
+        del self._menuInfo[menuName]
+        if parentName is None:
+            parentMenu = self._menu
+        else:
+            parentMenu = self.component(parentName)
+
+        menu = self.component(menuName)
+        menuId = str(menu)
+        for item in range(parentMenu.index('end') + 1):
+            if parentMenu.type(item) == 'cascade':
+                itemMenu = str(parentMenu.entrycget(item, 'menu'))
+                if itemMenu == menuId:
+                    parentMenu.delete(item)
+                    del self._menuInfo[parentName][1][item]
+                    break
+
+        self.destroycomponent(menuName)
+
+    def disableall(self):
+        for index in range(len(self._menuInfo[None][1])):
+            self.entryconfigure(index, state = 'disabled')
+
+    def enableall(self):
+        for index in range(len(self._menuInfo[None][1])):
+            self.entryconfigure(index, state = 'normal')
+
+    def addmenu(self, menuName, balloonHelp, statusHelp = None,
+            traverseSpec = None, **kw):
+        if statusHelp is None:
+            statusHelp = balloonHelp
+        self._addmenu(None, menuName, balloonHelp, statusHelp,
+            traverseSpec, kw)
+
+    def addcascademenu(self, parentMenuName, menuName, statusHelp='',
+            traverseSpec = None, **kw):
+        self._addmenu(parentMenuName, menuName, None, statusHelp,
+            traverseSpec, kw)
+
+    def _addmenu(self, parentMenuName, menuName, balloonHelp, statusHelp,
+            traverseSpec, kw):
+
+        if (menuName) in self.components():
+            raise ValueError, 'menu "%s" already exists' % menuName
+
+        menukw = {}
+        if kw.has_key('tearoff'):
+            menukw['tearoff'] = kw['tearoff']
+            del kw['tearoff']
+        else:
+            menukw['tearoff'] = 0
+        if kw.has_key('name'):
+            menukw['name'] = kw['name']
+            del kw['name']
+
+        if not kw.has_key('label'):
+            kw['label'] = menuName
+
+        self._addHotkeyToOptions(parentMenuName, kw, traverseSpec)
+
+        if parentMenuName is None:
+            parentMenu = self._menu
+            balloon = self['balloon']
+            # Bug in Tk: balloon help not implemented
+            # if balloon is not None:
+            #     balloon.mainmenubind(parentMenu, balloonHelp, statusHelp)
+        else:
+            parentMenu = self.component(parentMenuName)
+
+        apply(parentMenu.add_cascade, (), kw)
+
+        menu = apply(self.createcomponent, (menuName,
+                (), 'Menu',
+                Tkinter.Menu, (parentMenu,)), menukw)
+        parentMenu.entryconfigure('end', menu = menu)
+
+        self._menuInfo[parentMenuName][1].append(statusHelp)
+        self._menuInfo[menuName] = (parentMenuName, [])
+
+        menu.bind('<Leave>', self._resetHelpmessage)
+        menu.bind('<Motion>', 
+            lambda event=None, self=self, menuName=menuName:
+                    self._menuHelp(event, menuName))
+
+    def addmenuitem(self, menuName, itemType, statusHelp = '',
+            traverseSpec = None, **kw):
+
+        menu = self.component(menuName)
+        if itemType != 'separator':
+            self._addHotkeyToOptions(menuName, kw, traverseSpec)
+
+        if itemType == 'command':
+            command = menu.add_command
+        elif itemType == 'separator':
+            command = menu.add_separator
+        elif itemType == 'checkbutton':
+            command = menu.add_checkbutton
+        elif itemType == 'radiobutton':
+            command = menu.add_radiobutton
+        elif itemType == 'cascade':
+            command = menu.add_cascade
+        else:
+            raise ValueError, 'unknown menuitem type "%s"' % itemType
+
+        self._menuInfo[menuName][1].append(statusHelp)
+        apply(command, (), kw)
+
+    def _addHotkeyToOptions(self, menuName, kw, traverseSpec):
+
+        if (not self['hotkeys'] or kw.has_key('underline') or
+                not kw.has_key('label')):
+            return
+
+        if type(traverseSpec) == types.IntType:
+            kw['underline'] = traverseSpec
+            return
+
+        if menuName is None:
+            menu = self._menu
+        else:
+            menu = self.component(menuName)
+        hotkeyList = []
+        end = menu.index('end')
+        if end is not None:
+            for item in range(end + 1):
+                if menu.type(item) not in ('separator', 'tearoff'):
+                    underline = \
+                            string.atoi(str(menu.entrycget(item, 'underline')))
+                    if underline != -1:
+                        label = str(menu.entrycget(item, 'label'))
+                        if underline < len(label):
+                            hotkey = string.lower(label[underline])
+                            if hotkey not in hotkeyList:
+                                hotkeyList.append(hotkey)
+
+        name = kw['label']
+
+        if type(traverseSpec) == types.StringType:
+            lowerLetter = string.lower(traverseSpec)
+            if traverseSpec in name and lowerLetter not in hotkeyList:
+                kw['underline'] = string.index(name, traverseSpec)
+        else:
+            targets = string.digits + string.letters
+            lowerName = string.lower(name)
+            for letter_index in range(len(name)):
+                letter = lowerName[letter_index]
+                if letter in targets and letter not in hotkeyList:
+                    kw['underline'] = letter_index
+                    break
+
+    def _menuHelp(self, event, menuName):
+        if menuName is None:
+            menu = self._menu
+            index = menu.index('@%d'% event.x)
+        else:
+            menu = self.component(menuName)
+            index = menu.index('@%d'% event.y)
+
+        balloon = self['balloon']
+        if balloon is not None:
+            if index is None:
+                balloon.showstatus('')
+            else:
+                if str(menu.cget('tearoff')) == '1':
+                    index = index - 1
+                if index >= 0:
+                    help = self._menuInfo[menuName][1][index]
+                    balloon.showstatus(help)
+
+    def _resetHelpmessage(self, event=None):
+        balloon = self['balloon']
+        if balloon is not None:
+            balloon.clearstatus()
+
+forwardmethods(MainMenuBar, Tkinter.Menu, '_hull')
+
+######################################################################
+### File: PmwMenuBar.py
+# Manager widget for menus.
+
+import string
+import types
+import Tkinter
+
+
+class MenuBar(MegaWidget):
+
+    def __init__(self, parent = None, **kw):
+
+        # Define the megawidget options.
+        
+        optiondefs = (
+            ('balloon',      None,       None),
+            ('hotkeys',      1,          INITOPT),
+            ('padx',         0,          INITOPT),
+        )
+        self.defineoptions(kw, optiondefs, dynamicGroups = ('Menu', 'Button'))
+
+        # Initialise the base class (after defining the options).
+        MegaWidget.__init__(self, parent)
+
+        self._menuInfo = {}
+        # Map from a menu name to a tuple of information about the menu.
+        # The first item in the tuple is the name of the parent menu (for
+        # toplevel menus this is None). The second item in the tuple is
+        # a list of status help messages for each item in the menu.
+        # The third item in the tuple is the id of the binding used
+        # to detect mouse motion to display status help.
+        # Information for the toplevel menubuttons is not stored here.
+
+        self._mydeletecommand = self.component('hull').tk.deletecommand
+        # Cache this method for use later.
+
+        # Check keywords and initialise options.
+        self.initialiseoptions()
+
+    def deletemenuitems(self, menuName, start, end = None):
+        self.component(menuName + '-menu').delete(start, end)
+        if end is None:
+            del self._menuInfo[menuName][1][start]
+        else:
+            self._menuInfo[menuName][1][start:end+1] = []
+
+    def deletemenu(self, menuName):
+        """Delete should be called for cascaded menus before main menus.
+        """
+
+        # Clean up binding for this menu.
+        parentName = self._menuInfo[menuName][0]
+        bindId = self._menuInfo[menuName][2]
+        _bindtag = 'PmwMenuBar' + str(self) + menuName
+        self.unbind_class(_bindtag, '<Motion>')
+        self._mydeletecommand(bindId) # unbind_class does not clean up
+        del self._menuInfo[menuName]
+
+        if parentName is None:
+            self.destroycomponent(menuName + '-button')
+        else:
+            parentMenu = self.component(parentName + '-menu')
+
+            menu = self.component(menuName + '-menu')
+            menuId = str(menu)
+            for item in range(parentMenu.index('end') + 1):
+                if parentMenu.type(item) == 'cascade':
+                    itemMenu = str(parentMenu.entrycget(item, 'menu'))
+                    if itemMenu == menuId:
+                        parentMenu.delete(item)
+                        del self._menuInfo[parentName][1][item]
+                        break
+
+        self.destroycomponent(menuName + '-menu')
+
+    def disableall(self):
+        for menuName in self._menuInfo.keys():
+            if self._menuInfo[menuName][0] is None:
+                menubutton = self.component(menuName + '-button')
+                menubutton.configure(state = 'disabled')
+
+    def enableall(self):
+        for menuName in self._menuInfo.keys():
+            if self._menuInfo[menuName][0] is None:
+                menubutton = self.component(menuName + '-button')
+                menubutton.configure(state = 'normal')
+
+    def addmenu(self, menuName, balloonHelp, statusHelp = None,
+            side = 'left', traverseSpec = None, **kw):
+
+        self._addmenu(None, menuName, balloonHelp, statusHelp,
+            traverseSpec, side, 'text', kw)
+
+    def addcascademenu(self, parentMenuName, menuName, statusHelp = '',
+            traverseSpec = None, **kw):
+
+        self._addmenu(parentMenuName, menuName, None, statusHelp,
+            traverseSpec, None, 'label', kw)
+
+    def _addmenu(self, parentMenuName, menuName, balloonHelp, statusHelp,
+            traverseSpec, side, textKey, kw):
+
+        if (menuName + '-menu') in self.components():
+            raise ValueError, 'menu "%s" already exists' % menuName
+
+        menukw = {}
+        if kw.has_key('tearoff'):
+            menukw['tearoff'] = kw['tearoff']
+            del kw['tearoff']
+        else:
+            menukw['tearoff'] = 0
+
+        if not kw.has_key(textKey):
+            kw[textKey] = menuName
+
+        self._addHotkeyToOptions(parentMenuName, kw, textKey, traverseSpec)
+
+        if parentMenuName is None:
+            button = apply(self.createcomponent, (menuName + '-button',
+                    (), 'Button',
+                    Tkinter.Menubutton, (self.interior(),)), kw)
+            button.pack(side=side, padx = self['padx'])
+            balloon = self['balloon']
+            if balloon is not None:
+                balloon.bind(button, balloonHelp, statusHelp)
+            parentMenu = button
+        else:
+            parentMenu = self.component(parentMenuName + '-menu')
+            apply(parentMenu.add_cascade, (), kw)
+            self._menuInfo[parentMenuName][1].append(statusHelp)
+
+        menu = apply(self.createcomponent, (menuName + '-menu',
+                (), 'Menu',
+                Tkinter.Menu, (parentMenu,)), menukw)
+        if parentMenuName is None:
+            button.configure(menu = menu)
+        else:
+            parentMenu.entryconfigure('end', menu = menu)
+
+        # Need to put this binding after the class bindings so that
+        # menu.index() does not lag behind.
+        _bindtag = 'PmwMenuBar' + str(self) + menuName
+        bindId = self.bind_class(_bindtag, '<Motion>',
+            lambda event=None, self=self, menuName=menuName:
+                    self._menuHelp(menuName))
+        menu.bindtags(menu.bindtags() + (_bindtag,))
+        menu.bind('<Leave>', self._resetHelpmessage)
+
+        self._menuInfo[menuName] = (parentMenuName, [], bindId)
+
+    def addmenuitem(self, menuName, itemType, statusHelp = '',
+            traverseSpec = None, **kw):
+
+        menu = self.component(menuName + '-menu')
+        if itemType != 'separator':
+            self._addHotkeyToOptions(menuName, kw, 'label', traverseSpec)
+
+        if itemType == 'command':
+            command = menu.add_command
+        elif itemType == 'separator':
+            command = menu.add_separator
+        elif itemType == 'checkbutton':
+            command = menu.add_checkbutton
+        elif itemType == 'radiobutton':
+            command = menu.add_radiobutton
+        elif itemType == 'cascade':
+            command = menu.add_cascade
+        else:
+            raise ValueError, 'unknown menuitem type "%s"' % itemType
+
+        self._menuInfo[menuName][1].append(statusHelp)
+        apply(command, (), kw)
+
+    def _addHotkeyToOptions(self, menuName, kw, textKey, traverseSpec):
+
+        if (not self['hotkeys'] or kw.has_key('underline') or
+                not kw.has_key(textKey)):
+            return
+
+        if type(traverseSpec) == types.IntType:
+            kw['underline'] = traverseSpec
+            return
+
+        hotkeyList = []
+        if menuName is None:
+            for menuName in self._menuInfo.keys():
+                if self._menuInfo[menuName][0] is None:
+                    menubutton = self.component(menuName + '-button')
+                    underline = string.atoi(str(menubutton.cget('underline')))
+                    if underline != -1:
+                        label = str(menubutton.cget(textKey))
+                        if underline < len(label):
+                            hotkey = string.lower(label[underline])
+                            if hotkey not in hotkeyList:
+                                hotkeyList.append(hotkey)
+        else:
+            menu = self.component(menuName + '-menu')
+            end = menu.index('end')
+            if end is not None:
+                for item in range(end + 1):
+                    if menu.type(item) not in ('separator', 'tearoff'):
+                        underline = string.atoi(
+                            str(menu.entrycget(item, 'underline')))
+                        if underline != -1:
+                            label = str(menu.entrycget(item, textKey))
+                            if underline < len(label):
+                                hotkey = string.lower(label[underline])
+                                if hotkey not in hotkeyList:
+                                    hotkeyList.append(hotkey)
+
+        name = kw[textKey]
+
+        if type(traverseSpec) == types.StringType:
+            lowerLetter = string.lower(traverseSpec)
+            if traverseSpec in name and lowerLetter not in hotkeyList:
+                kw['underline'] = string.index(name, traverseSpec)
+        else:
+            targets = string.digits + string.letters
+            lowerName = string.lower(name)
+            for letter_index in range(len(name)):
+                letter = lowerName[letter_index]
+                if letter in targets and letter not in hotkeyList:
+                    kw['underline'] = letter_index
+                    break
+
+    def _menuHelp(self, menuName):
+        menu = self.component(menuName + '-menu')
+        index = menu.index('active')
+
+        balloon = self['balloon']
+        if balloon is not None:
+            if index is None:
+                balloon.showstatus('')
+            else:
+                if str(menu.cget('tearoff')) == '1':
+                    index = index - 1
+                if index >= 0:
+                    help = self._menuInfo[menuName][1][index]
+                    balloon.showstatus(help)
+
+    def _resetHelpmessage(self, event=None):
+        balloon = self['balloon']
+        if balloon is not None:
+            balloon.clearstatus()
+
+######################################################################
+### File: PmwMessageBar.py
+# Class to display messages in an information line.
+
+import string
+import Tkinter
+
+
+class MessageBar(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	defaultMessageTypes = {
+			   # (priority, showtime, bells, logmessage)
+	    'systemerror'  : (5, 10, 2, 1),
+	    'usererror'    : (4, 5, 1, 0),
+	    'busy'         : (3, 0, 0, 0),
+	    'systemevent'  : (2, 5, 0, 0),
+	    'userevent'    : (2, 5, 0, 0),
+	    'help'         : (1, 5, 0, 0),
+	    'state'        : (0, 0, 0, 0),
+	}
+	optiondefs = (
+	    ('labelmargin',    0,                     INITOPT),
+	    ('labelpos',       None,                  INITOPT),
+	    ('messagetypes',   defaultMessageTypes,   INITOPT),
+	    ('silent',         0,                     None),
+	    ('sticky',         'ew',                  INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	self._messageBarEntry = self.createcomponent('entry',
+		(), None,
+		Tkinter.Entry, (interior,))
+
+        # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+        try:
+            self._messageBarEntry.configure(state = 'readonly')
+        except Tkinter.TclError:
+            self._messageBarEntry.configure(state = 'disabled')
+
+	self._messageBarEntry.grid(column=2, row=2, sticky=self['sticky'])
+	interior.grid_columnconfigure(2, weight=1)
+	interior.grid_rowconfigure(2, weight=1)
+
+	self.createlabel(interior)
+
+	# Initialise instance variables.
+	self._numPriorities = 0
+	for info in self['messagetypes'].values():
+	    if self._numPriorities < info[0]:
+		self._numPriorities = info[0]
+
+	self._numPriorities = self._numPriorities + 1
+	self._timer = [None] * self._numPriorities
+	self._messagetext = [''] * self._numPriorities
+	self._activemessage = [0] * self._numPriorities
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	for timerId in self._timer:
+	    if timerId is not None:
+		self.after_cancel(timerId)
+	self._timer = [None] * self._numPriorities
+	MegaWidget.destroy(self)
+
+    def message(self, type, text):
+        # Display a message in the message bar.
+
+	(priority, showtime, bells, logmessage) = self['messagetypes'][type]
+
+	if not self['silent']:
+	    for i in range(bells):
+		if i != 0:
+		    self.after(100)
+		self.bell()
+
+	self._activemessage[priority] = 1
+	if text is None:
+	    text = ''
+	self._messagetext[priority] = string.replace(text, '\n', ' ')
+	self._redisplayInfoMessage()
+
+	if logmessage:
+	    # Should log this text to a text widget.
+	    pass
+
+	if showtime > 0:
+	    if self._timer[priority] is not None:
+		self.after_cancel(self._timer[priority])
+
+	    # Define a callback to clear this message after a time.
+	    def _clearmessage(self=self, priority=priority):
+		self._clearActivemessage(priority)
+
+	    mseconds = int(showtime * 1000)
+	    self._timer[priority] = self.after(mseconds, _clearmessage)
+
+    def helpmessage(self, text):
+        if text is None:
+            self.resetmessages('help')
+        else:
+            self.message('help', text)
+
+    def resetmessages(self, type):
+	priority = self['messagetypes'][type][0]
+	self._clearActivemessage(priority)
+	for messagetype, info in self['messagetypes'].items():
+	    thisPriority = info[0]
+	    showtime = info[1]
+	    if thisPriority < priority and showtime != 0:
+		self._clearActivemessage(thisPriority)
+
+    def _clearActivemessage(self, priority):
+	self._activemessage[priority] = 0
+	if self._timer[priority] is not None:
+	    self.after_cancel(self._timer[priority])
+	    self._timer[priority] = None
+	self._redisplayInfoMessage()
+
+    def _redisplayInfoMessage(self):
+	text = ''
+        for priority in range(self._numPriorities - 1, -1, -1):
+	    if self._activemessage[priority]:
+		text = self._messagetext[priority]
+	        break
+	self._messageBarEntry.configure(state = 'normal')
+	self._messageBarEntry.delete(0, 'end')
+	self._messageBarEntry.insert('end', text)
+
+        # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+        try:
+            self._messageBarEntry.configure(state = 'readonly')
+        except Tkinter.TclError:
+            self._messageBarEntry.configure(state = 'disabled')
+
+forwardmethods(MessageBar, Tkinter.Entry, '_messageBarEntry')
+
+######################################################################
+### File: PmwMessageDialog.py
+# Based on iwidgets2.2.0/messagedialog.itk code.
+
+import Tkinter
+
+
+class MessageDialog(Dialog):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderx',       20,    INITOPT),
+	    ('bordery',       20,    INITOPT),
+	    ('iconmargin',    20,    INITOPT),
+	    ('iconpos',       None,  INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	Dialog.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	self._message = self.createcomponent('message',
+		(), None,
+		Tkinter.Label, (interior,))
+
+        iconpos = self['iconpos']
+	iconmargin = self['iconmargin']
+	borderx = self['borderx']
+	bordery = self['bordery']
+	border_right = 2
+	border_bottom = 2
+	if iconpos is None:
+	    self._message.grid(column = 1, row = 1)
+	else:
+	    self._icon = self.createcomponent('icon',
+		    (), None,
+		    Tkinter.Label, (interior,))
+	    if iconpos not in 'nsew':
+		raise ValueError, \
+		    'bad iconpos option "%s":  should be n, s, e, or w' \
+			% iconpos
+
+	    if iconpos in 'nw':
+		icon = 1
+		message = 3
+	    else:
+		icon = 3
+		message = 1
+
+	    if iconpos in 'ns':
+		# vertical layout
+		self._icon.grid(column = 1, row = icon)
+		self._message.grid(column = 1, row = message)
+		interior.grid_rowconfigure(2, minsize = iconmargin)
+		border_bottom = 4
+	    else:
+		# horizontal layout
+		self._icon.grid(column = icon, row = 1)
+		self._message.grid(column = message, row = 1)
+		interior.grid_columnconfigure(2, minsize = iconmargin)
+		border_right = 4
+
+	interior.grid_columnconfigure(0, minsize = borderx)
+	interior.grid_rowconfigure(0, minsize = bordery)
+	interior.grid_columnconfigure(border_right, minsize = borderx)
+	interior.grid_rowconfigure(border_bottom, minsize = bordery)
+
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+######################################################################
+### File: PmwNoteBook.py
+import string
+import types
+import Tkinter
+
+
+class NoteBook(MegaArchetype):
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+        optiondefs = (
+	    ('hull_highlightthickness',  0,           None),
+	    ('hull_borderwidth',         0,           None),
+            ('arrownavigation',          1,           INITOPT),
+            ('borderwidth',              2,           INITOPT),
+            ('createcommand',            None,        None),
+            ('lowercommand',             None,        None),
+            ('pagemargin',               4,           INITOPT),
+            ('raisecommand',             None,        None),
+	    ('tabpos',                   'n',         INITOPT),
+        )
+	self.defineoptions(kw, optiondefs, dynamicGroups = ('Page', 'Tab'))
+
+	# Initialise the base class (after defining the options).
+	MegaArchetype.__init__(self, parent, Tkinter.Canvas)
+
+        self.bind('<Map>', self._handleMap)
+        self.bind('<Configure>', self._handleConfigure)
+
+        tabpos = self['tabpos']
+	if tabpos is not None and tabpos != 'n':
+            raise ValueError, \
+                'bad tabpos option %s:  should be n or None' % repr(tabpos)
+        self._withTabs = (tabpos is not None)
+        self._pageMargin = self['pagemargin']
+        self._borderWidth = self['borderwidth']
+
+        # Use a dictionary as a set of bits indicating what needs to
+        # be redisplayed the next time _layout() is called.  If
+        # dictionary contains 'topPage' key, the value is the new top
+        # page to be displayed.  None indicates that all pages have
+        # been deleted and that _layout() should draw a border under where
+        # the tabs should be.
+        self._pending = {}
+        self._pending['size'] = 1
+        self._pending['borderColor'] = 1
+        self._pending['topPage'] = None
+        if self._withTabs:
+            self._pending['tabs'] = 1
+
+        self._canvasSize = None       # This gets set by <Configure> events
+
+        # Set initial height of space for tabs
+        if self._withTabs:
+            self.tabBottom = 35
+        else:
+            self.tabBottom = 0
+
+        self._lightBorderColor, self._darkBorderColor = \
+                Color.bordercolors(self, self['hull_background'])
+
+	self._pageNames   = []        # List of page names
+
+        # Map from page name to page info.  Each item is itself a
+        # dictionary containing the following items:
+        #   page           the Tkinter.Frame widget for the page
+        #   created        set to true the first time the page is raised
+        #   tabbutton      the Tkinter.Button widget for the button (if any)
+        #   tabreqwidth    requested width of the tab
+        #   tabreqheight   requested height of the tab
+        #   tabitems       the canvas items for the button: the button
+        #                  window item, the lightshadow and the darkshadow
+        #   left           the left and right canvas coordinates of the tab
+        #   right
+	self._pageAttrs   = {}
+
+        # Name of page currently on top (actually displayed, using
+        # create_window, not pending).  Ignored if current top page
+        # has been deleted or new top page is pending.  None indicates
+        # no pages in notebook.
+	self._topPageName = None
+
+        # Canvas items used:
+        #   Per tab: 
+        #       top and left shadow
+        #       right shadow
+        #       button
+        #   Per notebook: 
+        #       page
+        #       top page
+        #       left shadow
+        #       bottom and right shadow
+        #       top (one or two items)
+
+        # Canvas tags used:
+        #   lighttag      - top and left shadows of tabs and page
+        #   darktag       - bottom and right shadows of tabs and page
+        #                   (if no tabs then these are reversed)
+        #                   (used to color the borders by recolorborders)
+
+        # Create page border shadows.
+        if self._withTabs:
+            self._pageLeftBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._lightBorderColor, tags = 'lighttag')
+            self._pageBottomRightBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._darkBorderColor, tags = 'darktag')
+            self._pageTop1Border = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._darkBorderColor, tags = 'lighttag')
+            self._pageTop2Border = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._darkBorderColor, tags = 'lighttag')
+        else:
+            self._pageLeftBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._darkBorderColor, tags = 'darktag')
+            self._pageBottomRightBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._lightBorderColor, tags = 'lighttag')
+            self._pageTopBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+                fill = self._darkBorderColor, tags = 'darktag')
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def insert(self, pageName, before = 0, **kw):
+	if self._pageAttrs.has_key(pageName):
+	    msg = 'Page "%s" already exists.' % pageName
+	    raise ValueError, msg
+
+        # Do this early to catch bad <before> spec before creating any items.
+	beforeIndex = self.index(before, 1)
+
+        pageOptions = {}
+        if self._withTabs:
+            # Default tab button options.
+            tabOptions = {
+                'text' : pageName,
+                'borderwidth' : 0,
+            }
+
+        # Divide the keyword options into the 'page_' and 'tab_' options.
+        for key in kw.keys():
+            if key[:5] == 'page_':
+                pageOptions[key[5:]] = kw[key]
+                del kw[key]
+            elif self._withTabs and key[:4] == 'tab_':
+                tabOptions[key[4:]] = kw[key]
+                del kw[key]
+            else:
+		raise KeyError, 'Unknown option "' + key + '"'
+
+        # Create the frame to contain the page.
+	page = apply(self.createcomponent, (pageName,
+		(), 'Page',
+		Tkinter.Frame, self._hull), pageOptions)
+
+        attributes = {}
+        attributes['page'] = page
+        attributes['created'] = 0
+
+        if self._withTabs:
+            # Create the button for the tab.
+            def raiseThisPage(self = self, pageName = pageName):
+                self.selectpage(pageName)
+            tabOptions['command'] = raiseThisPage
+            tab = apply(self.createcomponent, (pageName + '-tab',
+                    (), 'Tab',
+                    Tkinter.Button, self._hull), tabOptions)
+
+            if self['arrownavigation']:
+                # Allow the use of the arrow keys for Tab navigation:
+                def next(event, self = self, pageName = pageName):
+                    self.nextpage(pageName)
+                def prev(event, self = self, pageName = pageName):
+                    self.previouspage(pageName)
+                tab.bind('<Left>', prev)
+                tab.bind('<Right>', next)
+
+            attributes['tabbutton'] = tab
+            attributes['tabreqwidth'] = tab.winfo_reqwidth()
+            attributes['tabreqheight'] = tab.winfo_reqheight()
+
+            # Create the canvas item to manage the tab's button and the items
+            # for the tab's shadow.
+            windowitem = self.create_window(0, 0, window = tab, anchor = 'nw')
+            lightshadow = self.create_polygon(0, 0, 0, 0, 0, 0,
+                tags = 'lighttag', fill = self._lightBorderColor)
+            darkshadow = self.create_polygon(0, 0, 0, 0, 0, 0,
+                tags = 'darktag', fill = self._darkBorderColor)
+            attributes['tabitems'] = (windowitem, lightshadow, darkshadow)
+            self._pending['tabs'] = 1
+
+        self._pageAttrs[pageName] = attributes
+	self._pageNames.insert(beforeIndex, pageName)
+
+        # If this is the first page added, make it the new top page
+        # and call the create and raise callbacks.
+        if self.getcurselection() is None:
+            self._pending['topPage'] = pageName
+            self._raiseNewTop(pageName)
+
+        self._layout()
+        return page
+  		
+    def add(self, pageName, **kw):
+        return apply(self.insert, (pageName, len(self._pageNames)), kw)
+
+    def delete(self, *pageNames):
+        newTopPage = 0
+        for page in pageNames:
+            pageIndex = self.index(page)
+            pageName = self._pageNames[pageIndex]
+            pageInfo = self._pageAttrs[pageName]
+
+            if self.getcurselection() == pageName:
+                if len(self._pageNames) == 1:
+                    newTopPage = 0
+                    self._pending['topPage'] = None
+                elif pageIndex == len(self._pageNames) - 1:
+                    newTopPage = 1
+                    self._pending['topPage'] = self._pageNames[pageIndex - 1]
+                else:
+                    newTopPage = 1
+                    self._pending['topPage'] = self._pageNames[pageIndex + 1]
+
+            if self._topPageName == pageName:
+                self._hull.delete(self._topPageItem)
+                self._topPageName = None
+                                
+            if self._withTabs:
+                self.destroycomponent(pageName + '-tab')
+                apply(self._hull.delete, pageInfo['tabitems'])
+            self.destroycomponent(pageName)
+            del self._pageAttrs[pageName]
+            del self._pageNames[pageIndex]
+
+        # If the old top page was deleted and there are still pages
+        # left in the notebook, call the create and raise callbacks.
+        if newTopPage:
+            pageName = self._pending['topPage']
+            self._raiseNewTop(pageName)
+
+        if self._withTabs:
+            self._pending['tabs'] = 1
+        self._layout()
+
+    def page(self, pageIndex):
+        pageName = self._pageNames[self.index(pageIndex)]
+	return self._pageAttrs[pageName]['page']
+
+    def pagenames(self):
+	return list(self._pageNames)
+
+    def getcurselection(self):
+        if self._pending.has_key('topPage'):
+            return self._pending['topPage']
+        else:
+            return self._topPageName
+
+    def tab(self, pageIndex):
+        if self._withTabs:
+            pageName = self._pageNames[self.index(pageIndex)]
+            return self._pageAttrs[pageName]['tabbutton']
+        else:
+            return None
+
+    def index(self, index, forInsert = 0):
+	listLength = len(self._pageNames)
+	if type(index) == types.IntType:
+	    if forInsert and index <= listLength:
+		return index
+	    elif not forInsert and index < listLength:
+		return index
+	    else:
+		raise ValueError, 'index "%s" is out of range' % index
+	elif index is END:
+	    if forInsert:
+		return listLength
+	    elif listLength > 0:
+		return listLength - 1
+	    else:
+		raise ValueError, 'NoteBook has no pages'
+	elif index is SELECT:
+	    if listLength == 0:
+		raise ValueError, 'NoteBook has no pages'
+            return self._pageNames.index(self.getcurselection())
+	else:
+            if index in self._pageNames:
+                return self._pageNames.index(index)
+	    validValues = 'a name, a number, END or SELECT'
+	    raise ValueError, \
+                'bad index "%s": must be %s' % (index, validValues)
+
+    def selectpage(self, page):
+        pageName = self._pageNames[self.index(page)]
+        oldTopPage = self.getcurselection()
+        if pageName != oldTopPage:
+            self._pending['topPage'] = pageName
+            if oldTopPage == self._topPageName:
+                self._hull.delete(self._topPageItem)
+            cmd = self['lowercommand']
+            if cmd is not None:
+                cmd(oldTopPage)
+            self._raiseNewTop(pageName)
+
+            self._layout()
+
+        # Set focus to the tab of new top page:
+        if self._withTabs and self['arrownavigation']:
+            self._pageAttrs[pageName]['tabbutton'].focus_set()
+
+    def previouspage(self, pageIndex = None):
+        if pageIndex is None:
+            curpage = self.index(SELECT)
+        else:
+            curpage = self.index(pageIndex)
+	if curpage > 0:
+	    self.selectpage(curpage - 1)
+
+    def nextpage(self, pageIndex = None):
+        if pageIndex is None:
+            curpage = self.index(SELECT)
+        else:
+            curpage = self.index(pageIndex)
+	if curpage < len(self._pageNames) - 1:
+	    self.selectpage(curpage + 1)
+
+    def setnaturalsize(self, pageNames = None):
+        self.update_idletasks()
+        maxPageWidth = 1
+        maxPageHeight = 1
+        if pageNames is None:
+            pageNames = self.pagenames()
+        for pageName in pageNames:
+            pageInfo = self._pageAttrs[pageName]
+            page = pageInfo['page']
+            w = page.winfo_reqwidth()
+            h = page.winfo_reqheight()
+            if maxPageWidth < w:
+                maxPageWidth = w
+            if maxPageHeight < h:
+                maxPageHeight = h
+        pageBorder = self._borderWidth + self._pageMargin
+        width = maxPageWidth + pageBorder * 2
+        height = maxPageHeight + pageBorder * 2
+
+        if self._withTabs:
+            maxTabHeight = 0
+            for pageInfo in self._pageAttrs.values():
+                if maxTabHeight < pageInfo['tabreqheight']:
+                    maxTabHeight = pageInfo['tabreqheight']
+            height = height + maxTabHeight + self._borderWidth * 1.5
+
+        # Note that, since the hull is a canvas, the width and height
+        # options specify the geometry *inside* the borderwidth and
+        # highlightthickness.
+        self.configure(hull_width = width, hull_height = height)
+
+    def recolorborders(self):
+        self._pending['borderColor'] = 1
+        self._layout()
+
+    def _handleMap(self, event):
+        self._layout()
+
+    def _handleConfigure(self, event):
+        self._canvasSize = (event.width, event.height)
+        self._pending['size'] = 1
+        self._layout()
+
+    def _raiseNewTop(self, pageName):
+        if not self._pageAttrs[pageName]['created']:
+            self._pageAttrs[pageName]['created'] = 1
+            cmd = self['createcommand']
+            if cmd is not None:
+                cmd(pageName)
+        cmd = self['raisecommand']
+        if cmd is not None:
+            cmd(pageName)
+
+    # This is the vertical layout of the notebook, from top (assuming
+    # tabpos is 'n'):
+    #     hull highlightthickness (top)
+    #     hull borderwidth (top)
+    #     borderwidth (top border of tabs)
+    #     borderwidth * 0.5 (space for bevel)
+    #     tab button (maximum of requested height of all tab buttons)
+    #     borderwidth (border between tabs and page)
+    #     pagemargin (top)
+    #     the page itself
+    #     pagemargin (bottom)
+    #     borderwidth (border below page)
+    #     hull borderwidth (bottom)
+    #     hull highlightthickness (bottom)
+    #
+    # canvasBorder is sum of top two elements.
+    # tabBottom is sum of top five elements.
+    #
+    # Horizontal layout (and also vertical layout when tabpos is None):
+    #     hull highlightthickness
+    #     hull borderwidth
+    #     borderwidth
+    #     pagemargin
+    #     the page itself
+    #     pagemargin
+    #     borderwidth
+    #     hull borderwidth
+    #     hull highlightthickness
+    #
+    def _layout(self):
+        if not self.winfo_ismapped() or self._canvasSize is None:
+            # Don't layout if the window is not displayed, or we
+            # haven't yet received a <Configure> event.
+            return
+
+        hullWidth, hullHeight = self._canvasSize
+        borderWidth = self._borderWidth
+        canvasBorder = string.atoi(self._hull['borderwidth']) + \
+            string.atoi(self._hull['highlightthickness'])
+        if not self._withTabs:
+            self.tabBottom = canvasBorder
+        oldTabBottom = self.tabBottom
+
+        if self._pending.has_key('borderColor'):
+            self._lightBorderColor, self._darkBorderColor = \
+                    Color.bordercolors(self, self['hull_background'])
+
+        # Draw all the tabs.
+        if self._withTabs and (self._pending.has_key('tabs') or
+                self._pending.has_key('size')):
+            # Find total requested width and maximum requested height
+            # of tabs.
+            sumTabReqWidth = 0
+            maxTabHeight = 0
+            for pageInfo in self._pageAttrs.values():
+                sumTabReqWidth = sumTabReqWidth + pageInfo['tabreqwidth']
+                if maxTabHeight < pageInfo['tabreqheight']:
+                    maxTabHeight = pageInfo['tabreqheight']
+            if maxTabHeight != 0:
+                # Add the top tab border plus a bit for the angled corners
+                self.tabBottom = canvasBorder + maxTabHeight + borderWidth * 1.5
+
+            # Prepare for drawing the border around each tab button.
+            tabTop = canvasBorder
+            tabTop2 = tabTop + borderWidth
+            tabTop3 = tabTop + borderWidth * 1.5
+            tabBottom2 = self.tabBottom
+            tabBottom = self.tabBottom + borderWidth
+
+            numTabs = len(self._pageNames)
+            availableWidth = hullWidth - 2 * canvasBorder - \
+                numTabs * 2 * borderWidth
+            x = canvasBorder
+            cumTabReqWidth = 0
+            cumTabWidth = 0
+
+            # Position all the tabs.
+            for pageName in self._pageNames:
+                pageInfo = self._pageAttrs[pageName]
+                (windowitem, lightshadow, darkshadow) = pageInfo['tabitems']
+                if sumTabReqWidth <= availableWidth:
+                    tabwidth = pageInfo['tabreqwidth']
+                else:
+                    # This ugly calculation ensures that, when the
+                    # notebook is not wide enough for the requested
+                    # widths of the tabs, the total width given to
+                    # the tabs exactly equals the available width,
+                    # without rounding errors.
+                    cumTabReqWidth = cumTabReqWidth + pageInfo['tabreqwidth']
+                    tmp = (2*cumTabReqWidth*availableWidth + sumTabReqWidth) \
+                            / (2 * sumTabReqWidth)
+                    tabwidth = tmp - cumTabWidth
+                    cumTabWidth = tmp
+
+                # Position the tab's button canvas item.
+                self.coords(windowitem, x + borderWidth, tabTop3)
+                self.itemconfigure(windowitem,
+                    width = tabwidth, height = maxTabHeight)
+
+                # Make a beautiful border around the tab.
+                left = x
+                left2 = left + borderWidth
+                left3 = left + borderWidth * 1.5
+                right = left + tabwidth + 2 * borderWidth
+                right2 = left + tabwidth + borderWidth
+                right3 = left + tabwidth + borderWidth * 0.5
+
+                self.coords(lightshadow, 
+                    left, tabBottom2, left, tabTop2, left2, tabTop,
+                    right2, tabTop, right3, tabTop2, left3, tabTop2,
+                    left2, tabTop3, left2, tabBottom,
+                    )
+                self.coords(darkshadow, 
+                    right2, tabTop, right, tabTop2, right, tabBottom2,
+                    right2, tabBottom, right2, tabTop3, right3, tabTop2,
+                    )
+                pageInfo['left'] = left
+                pageInfo['right'] = right
+
+                x = x + tabwidth + 2 * borderWidth
+
+        # Redraw shadow under tabs so that it appears that tab for old
+        # top page is lowered and that tab for new top page is raised.
+        if self._withTabs and (self._pending.has_key('topPage') or
+                self._pending.has_key('tabs') or self._pending.has_key('size')):
+
+            if self.getcurselection() is None:
+                # No pages, so draw line across top of page area.
+                self.coords(self._pageTop1Border,
+                    canvasBorder, self.tabBottom,
+                    hullWidth - canvasBorder, self.tabBottom,
+                    hullWidth - canvasBorder - borderWidth,
+                        self.tabBottom + borderWidth,
+                    borderWidth + canvasBorder, self.tabBottom + borderWidth,
+                    )
+
+                # Ignore second top border.
+                self.coords(self._pageTop2Border, 0, 0, 0, 0, 0, 0)
+            else:
+                # Draw two lines, one on each side of the tab for the
+                # top page, so that the tab appears to be raised.
+                pageInfo = self._pageAttrs[self.getcurselection()]
+                left = pageInfo['left']
+                right = pageInfo['right']
+                self.coords(self._pageTop1Border,
+                    canvasBorder, self.tabBottom,
+                    left, self.tabBottom,
+                    left + borderWidth, self.tabBottom + borderWidth,
+                    canvasBorder + borderWidth, self.tabBottom + borderWidth,
+                    )
+
+                self.coords(self._pageTop2Border,
+                    right, self.tabBottom,
+                    hullWidth - canvasBorder, self.tabBottom,
+                    hullWidth - canvasBorder - borderWidth,
+                        self.tabBottom + borderWidth,
+                    right - borderWidth, self.tabBottom + borderWidth,
+                    )
+
+            # Prevent bottom of dark border of tabs appearing over
+            # page top border.
+            self.tag_raise(self._pageTop1Border)
+            self.tag_raise(self._pageTop2Border)
+
+        # Position the page border shadows.
+        if self._pending.has_key('size') or oldTabBottom != self.tabBottom:
+
+            self.coords(self._pageLeftBorder,
+                canvasBorder, self.tabBottom,
+                borderWidth + canvasBorder,
+                    self.tabBottom + borderWidth,
+                borderWidth + canvasBorder,
+                    hullHeight - canvasBorder - borderWidth,
+                canvasBorder, hullHeight - canvasBorder,
+                )
+
+            self.coords(self._pageBottomRightBorder,
+                hullWidth - canvasBorder, self.tabBottom,
+                hullWidth - canvasBorder, hullHeight - canvasBorder,
+                canvasBorder, hullHeight - canvasBorder,
+                borderWidth + canvasBorder,
+                    hullHeight - canvasBorder - borderWidth,
+                hullWidth - canvasBorder - borderWidth,
+                    hullHeight - canvasBorder - borderWidth,
+                hullWidth - canvasBorder - borderWidth,
+                    self.tabBottom + borderWidth,
+                )
+
+            if not self._withTabs:
+                self.coords(self._pageTopBorder,
+                    canvasBorder, self.tabBottom,
+                    hullWidth - canvasBorder, self.tabBottom,
+                    hullWidth - canvasBorder - borderWidth,
+                        self.tabBottom + borderWidth,
+                    borderWidth + canvasBorder, self.tabBottom + borderWidth,
+                    )
+
+        # Color borders.
+        if self._pending.has_key('borderColor'):
+            self.itemconfigure('lighttag', fill = self._lightBorderColor)
+            self.itemconfigure('darktag', fill = self._darkBorderColor)
+
+        newTopPage = self._pending.get('topPage')
+        pageBorder = borderWidth + self._pageMargin
+
+        # Raise new top page.
+        if newTopPage is not None:
+            self._topPageName = newTopPage
+            self._topPageItem = self.create_window(
+                pageBorder + canvasBorder, self.tabBottom + pageBorder,
+                window = self._pageAttrs[newTopPage]['page'],
+                anchor = 'nw',
+                )
+
+        # Change position of top page if tab height has changed.
+        if self._topPageName is not None and oldTabBottom != self.tabBottom:
+            self.coords(self._topPageItem,
+                    pageBorder + canvasBorder, self.tabBottom + pageBorder)
+
+        # Change size of top page if,
+        #   1) there is a new top page.
+        #   2) canvas size has changed, but not if there is no top
+        #      page (eg:  initially or when all pages deleted).
+        #   3) tab height has changed, due to difference in the height of a tab
+        if (newTopPage is not None or \
+                self._pending.has_key('size') and self._topPageName is not None
+                or oldTabBottom != self.tabBottom):
+            self.itemconfigure(self._topPageItem,
+                width = hullWidth - 2 * canvasBorder - pageBorder * 2,
+                height = hullHeight - 2 * canvasBorder - pageBorder * 2 -
+                    (self.tabBottom - canvasBorder),
+                )
+
+        self._pending = {}
+
+# Need to do forwarding to get the pack, grid, etc methods. 
+# Unfortunately this means that all the other canvas methods are also
+# forwarded.
+forwardmethods(NoteBook, Tkinter.Canvas, '_hull')
+
+######################################################################
+### File: PmwOptionMenu.py
+import types
+import Tkinter
+
+
+class OptionMenu(MegaWidget):
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('command',        None,       None),
+            ('items',          (),         INITOPT),
+            ('initialitem',    None,       INITOPT),
+	    ('labelmargin',    0,          INITOPT),
+	    ('labelpos',       None,       INITOPT),
+	    ('sticky',         'ew',       INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	self._menubutton = self.createcomponent('menubutton',
+		(), None,
+		Tkinter.Menubutton, (interior,),
+		borderwidth = 2,
+		indicatoron = 1,
+		relief = 'raised',
+		anchor = 'c',
+		highlightthickness = 2,
+		direction = 'flush',
+                takefocus = 1,
+	)
+	self._menubutton.grid(column = 2, row = 2, sticky = self['sticky'])
+
+	self._menu = self.createcomponent('menu',
+		(), None,
+		Tkinter.Menu, (self._menubutton,),
+		tearoff=0
+	)
+	self._menubutton.configure(menu = self._menu)
+
+	interior.grid_columnconfigure(2, weight = 1)
+	interior.grid_rowconfigure(2, weight = 1)
+
+        # Create the label.
+        self.createlabel(interior)
+
+        # Add the items specified by the initialisation option.
+	self._itemList = []
+        self.setitems(self['items'], self['initialitem'])
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def setitems(self, items, index = None):
+
+        # Clean up old items and callback commands.
+        for oldIndex in range(len(self._itemList)):
+            tclCommandName = str(self._menu.entrycget(oldIndex, 'command'))
+            if tclCommandName != '':   
+                self._menu.deletecommand(tclCommandName)
+        self._menu.delete(0, 'end')
+	self._itemList = list(items)
+
+	# Set the items in the menu component.
+        for item in items:
+            self._menu.add_command(label = item,
+		command = lambda self = self, item = item: self._invoke(item))
+
+	# Set the currently selected value.
+	if index is None:
+            var = str(self._menubutton.cget('textvariable'))
+	    if var != '':
+		# None means do not change text variable.
+		return
+	    if len(items) == 0:
+		text = ''
+	    elif str(self._menubutton.cget('text')) in items:
+                # Do not change selection if it is still valid
+		return
+	    else:
+		text = items[0]
+	else:
+	    index = self.index(index)
+	    text = self._itemList[index]
+
+        self.setvalue(text)
+
+    def getcurselection(self):
+	var = str(self._menubutton.cget('textvariable'))
+	if var == '':
+	    return str(self._menubutton.cget('text'))
+	else:
+	    return self._menu.tk.globalgetvar(var)
+
+    def getvalue(self):
+        return self.getcurselection()
+
+    def setvalue(self, text):
+	var = str(self._menubutton.cget('textvariable'))
+	if var == '':
+	    self._menubutton.configure(text = text)
+	else:
+	    self._menu.tk.globalsetvar(var, text)
+
+    def index(self, index):
+	listLength = len(self._itemList)
+	if type(index) == types.IntType:
+	    if index < listLength:
+		return index
+	    else:
+		raise ValueError, 'index "%s" is out of range' % index
+	elif index is END:
+	    if listLength > 0:
+		return listLength - 1
+	    else:
+		raise ValueError, 'OptionMenu has no items'
+	else:
+	    if index is SELECT:
+		if listLength > 0:
+		    index = self.getcurselection()
+		else:
+		    raise ValueError, 'OptionMenu has no items'
+            if index in self._itemList:
+                return self._itemList.index(index)
+	    raise ValueError, \
+		    'bad index "%s": must be a ' \
+                    'name, a number, END or SELECT' % (index,)
+
+    def invoke(self, index = SELECT):
+	index = self.index(index)
+	text = self._itemList[index]
+
+        return self._invoke(text)
+
+    def _invoke(self, text):
+        self.setvalue(text)
+
+	command = self['command']
+	if callable(command):
+	    return command(text)
+
+######################################################################
+### File: PmwPanedWidget.py
+# PanedWidget
+# a frame which may contain several resizable sub-frames
+
+import string
+import sys
+import types
+import Tkinter
+
+
+class PanedWidget(MegaWidget):
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+            ('command',            None,         None),
+            ('orient',             'vertical',   INITOPT),
+            ('separatorrelief',    'sunken',     INITOPT),
+            ('separatorthickness', 2,            INITOPT),
+            ('handlesize',         8,            INITOPT),
+            ('hull_width',         400,          None),
+            ('hull_height',        400,          None),
+	)
+	self.defineoptions(kw, optiondefs,
+                dynamicGroups = ('Frame', 'Separator', 'Handle'))
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	self.bind('<Configure>', self._handleConfigure)
+
+	if self['orient'] not in ('horizontal', 'vertical'):
+	    raise ValueError, 'bad orient option ' + repr(self['orient']) + \
+		': must be either \'horizontal\' or \'vertical\''
+
+        self._separatorThickness = self['separatorthickness']
+        self._handleSize = self['handlesize']
+	self._paneNames = []            # List of pane names
+	self._paneAttrs = {}            # Map from pane name to pane info
+
+	self._timerId = None
+	self._frame = {}
+	self._separator = []
+	self._button = []
+	self._totalSize = 0
+	self._movePending = 0
+	self._relsize = {}
+	self._relmin = {}
+	self._relmax = {}
+	self._size = {}
+	self._min = {}
+	self._max = {}
+	self._rootp = None
+	self._curSize = None
+	self._beforeLimit = None
+	self._afterLimit = None
+	self._buttonIsDown = 0
+	self._majorSize = 100
+	self._minorSize = 100
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def insert(self, name, before = 0, **kw):
+	# Parse <kw> for options.
+        self._initPaneOptions(name)
+	self._parsePaneOptions(name, kw)
+
+	insertPos = self._nameToIndex(before)
+	atEnd = (insertPos == len(self._paneNames))
+
+	# Add the frame.
+	self._paneNames[insertPos:insertPos] = [name]
+	self._frame[name] = self.createcomponent(name,
+		(), 'Frame',
+		Tkinter.Frame, (self.interior(),))
+
+	# Add separator, if necessary.
+	if len(self._paneNames) > 1:
+	    self._addSeparator()
+	else:
+	    self._separator.append(None)
+	    self._button.append(None)
+
+	# Add the new frame and adjust the PanedWidget
+	if atEnd:
+	    size = self._size[name]
+	    if size > 0 or self._relsize[name] is not None:
+		if self['orient'] == 'vertical':
+		    self._frame[name].place(x=0, relwidth=1,
+					    height=size, y=self._totalSize)
+		else:
+		    self._frame[name].place(y=0, relheight=1,
+					    width=size, x=self._totalSize)
+	    else:
+		if self['orient'] == 'vertical':
+		    self._frame[name].place(x=0, relwidth=1,
+					    y=self._totalSize)
+		else:
+		    self._frame[name].place(y=0, relheight=1,
+					    x=self._totalSize)
+	else:
+	    self._updateSizes()
+
+	self._totalSize = self._totalSize + self._size[name]
+	return self._frame[name]
+
+    def add(self, name, **kw):
+        return apply(self.insert, (name, len(self._paneNames)), kw)
+
+    def delete(self, name):
+	deletePos = self._nameToIndex(name)
+	name = self._paneNames[deletePos]
+	self.destroycomponent(name)
+	del self._paneNames[deletePos]
+	del self._frame[name]
+	del self._size[name]
+	del self._min[name]
+	del self._max[name]
+	del self._relsize[name]
+	del self._relmin[name]
+	del self._relmax[name]
+
+	last = len(self._paneNames)
+	del self._separator[last]
+	del self._button[last]
+        if last > 0:
+            self.destroycomponent(self._sepName(last))
+            self.destroycomponent(self._buttonName(last))
+
+	self._plotHandles()
+
+    def setnaturalsize(self):
+        self.update_idletasks()
+        totalWidth = 0
+        totalHeight = 0
+        maxWidth = 0
+        maxHeight = 0
+	for name in self._paneNames:
+            frame = self._frame[name]
+            w = frame.winfo_reqwidth()
+            h = frame.winfo_reqheight()
+            totalWidth = totalWidth + w
+            totalHeight = totalHeight + h
+            if maxWidth < w:
+                maxWidth = w
+            if maxHeight < h:
+                maxHeight = h
+
+        # Note that, since the hull is a frame, the width and height
+        # options specify the geometry *outside* the borderwidth and
+        # highlightthickness.
+        bw = string.atoi(str(self.cget('hull_borderwidth')))
+        hl = string.atoi(str(self.cget('hull_highlightthickness')))
+        extra = (bw + hl) * 2
+        if str(self.cget('orient')) == 'horizontal':
+            totalWidth = totalWidth + extra
+            maxHeight = maxHeight + extra
+            self.configure(hull_width = totalWidth, hull_height = maxHeight)
+        else:
+            totalHeight = (totalHeight + extra +
+                    (len(self._paneNames) - 1) * self._separatorThickness)
+            maxWidth = maxWidth + extra
+            self.configure(hull_width = maxWidth, hull_height = totalHeight)
+
+    def move(self, name, newPos, newPosOffset = 0):
+
+        # see if we can spare ourselves some work
+        numPanes = len(self._paneNames)
+        if numPanes < 2:
+            return
+
+        newPos = self._nameToIndex(newPos) + newPosOffset
+        if newPos < 0 or newPos >=numPanes:
+            return
+
+        deletePos = self._nameToIndex(name)
+
+        if deletePos == newPos:
+            # inserting over ourself is a no-op
+            return
+
+        # delete name from old position in list
+        name = self._paneNames[deletePos]
+        del self._paneNames[deletePos]
+
+        # place in new position
+        self._paneNames[newPos:newPos] = [name]
+
+        # force everything to redraw
+        self._plotHandles()
+        self._updateSizes()
+
+    def _nameToIndex(self, nameOrIndex):
+	try:
+	    pos = self._paneNames.index(nameOrIndex)
+	except ValueError:
+	    pos = nameOrIndex
+
+	return pos
+
+    def _initPaneOptions(self, name):
+	# Set defaults.
+	self._size[name] = 0
+	self._relsize[name] = None
+	self._min[name] = 0
+	self._relmin[name] = None
+	self._max[name] = 100000
+	self._relmax[name] = None
+
+    def _parsePaneOptions(self, name, args):
+	# Parse <args> for options.
+	for arg, value in args.items():
+	    if type(value) == types.FloatType:
+		relvalue = value
+		value = self._absSize(relvalue)
+	    else:
+		relvalue = None
+
+	    if arg == 'size':
+		self._size[name], self._relsize[name] = value, relvalue
+	    elif arg == 'min':
+		self._min[name], self._relmin[name] = value, relvalue
+	    elif arg == 'max':
+		self._max[name], self._relmax[name] = value, relvalue
+	    else:
+		raise ValueError, 'keyword must be "size", "min", or "max"'
+
+    def _absSize(self, relvalue):
+	return int(round(relvalue * self._majorSize))
+
+    def _sepName(self, n):
+	return 'separator-%d' % n
+
+    def _buttonName(self, n):
+	return 'handle-%d' % n
+
+    def _addSeparator(self):
+	n = len(self._paneNames) - 1
+
+	downFunc = lambda event, s = self, num=n: s._btnDown(event, num)
+	upFunc = lambda event, s = self, num=n: s._btnUp(event, num)
+	moveFunc = lambda event, s = self, num=n: s._btnMove(event, num)
+
+	# Create the line dividing the panes.
+	sep = self.createcomponent(self._sepName(n),
+		(), 'Separator',
+		Tkinter.Frame, (self.interior(),),
+		borderwidth = 1,
+		relief = self['separatorrelief'])
+	self._separator.append(sep)
+
+	sep.bind('<ButtonPress-1>', downFunc)
+	sep.bind('<Any-ButtonRelease-1>', upFunc)
+	sep.bind('<B1-Motion>', moveFunc)
+
+	if self['orient'] == 'vertical':
+	    cursor = 'sb_v_double_arrow'
+	    sep.configure(height = self._separatorThickness,
+                    width = 10000, cursor = cursor)
+	else:
+	    cursor = 'sb_h_double_arrow'
+	    sep.configure(width = self._separatorThickness,
+                    height = 10000, cursor = cursor)
+
+	self._totalSize = self._totalSize + self._separatorThickness
+
+	# Create the handle on the dividing line.
+	handle = self.createcomponent(self._buttonName(n),
+		(), 'Handle',
+		Tkinter.Frame, (self.interior(),),
+		    relief = 'raised',
+		    borderwidth = 1,
+		    width = self._handleSize,
+		    height = self._handleSize,
+		    cursor = cursor,
+		)
+	self._button.append(handle)
+
+	handle.bind('<ButtonPress-1>', downFunc)
+	handle.bind('<Any-ButtonRelease-1>', upFunc)
+	handle.bind('<B1-Motion>', moveFunc)
+
+	self._plotHandles()
+
+	for i in range(1, len(self._paneNames)):
+	    self._separator[i].tkraise()
+	for i in range(1, len(self._paneNames)):
+	    self._button[i].tkraise()
+
+    def _btnUp(self, event, item):
+	self._buttonIsDown = 0
+	self._updateSizes()
+	try:
+	    self._button[item].configure(relief='raised')
+	except:
+	    pass
+
+    def _btnDown(self, event, item):
+	self._button[item].configure(relief='sunken')
+	self._getMotionLimit(item)
+	self._buttonIsDown = 1
+	self._movePending = 0
+
+    def _handleConfigure(self, event = None):
+	self._getNaturalSizes()
+	if self._totalSize == 0:
+	    return
+
+	iterRange = list(self._paneNames)
+	iterRange.reverse()
+	if self._majorSize > self._totalSize:
+	    n = self._majorSize - self._totalSize
+	    self._iterate(iterRange, self._grow, n)
+	elif self._majorSize < self._totalSize:
+	    n = self._totalSize - self._majorSize
+	    self._iterate(iterRange, self._shrink, n)
+
+	self._plotHandles()
+	self._updateSizes()
+
+    def _getNaturalSizes(self):
+	# Must call this in order to get correct winfo_width, winfo_height
+	self.update_idletasks()
+
+	self._totalSize = 0
+
+	if self['orient'] == 'vertical':
+	    self._majorSize = self.winfo_height()
+	    self._minorSize = self.winfo_width()
+	    majorspec = Tkinter.Frame.winfo_reqheight
+	else:
+	    self._majorSize = self.winfo_width()
+	    self._minorSize = self.winfo_height()
+	    majorspec = Tkinter.Frame.winfo_reqwidth
+
+        bw = string.atoi(str(self.cget('hull_borderwidth')))
+        hl = string.atoi(str(self.cget('hull_highlightthickness')))
+        extra = (bw + hl) * 2
+        self._majorSize = self._majorSize - extra
+        self._minorSize = self._minorSize - extra
+
+	if self._majorSize < 0:
+	    self._majorSize = 0
+	if self._minorSize < 0:
+	    self._minorSize = 0
+
+	for name in self._paneNames:
+	    # adjust the absolute sizes first...
+	    if self._relsize[name] is None:
+		#special case
+		if self._size[name] == 0:
+		    self._size[name] = apply(majorspec, (self._frame[name],))
+		    self._setrel(name)
+	    else:
+		self._size[name] = self._absSize(self._relsize[name])
+
+	    if self._relmin[name] is not None:
+		self._min[name] = self._absSize(self._relmin[name])
+	    if self._relmax[name] is not None:
+		self._max[name] = self._absSize(self._relmax[name])
+
+	    # now adjust sizes
+	    if self._size[name] < self._min[name]:
+		self._size[name] = self._min[name]
+		self._setrel(name)
+
+	    if self._size[name] > self._max[name]:
+		self._size[name] = self._max[name]
+		self._setrel(name)
+
+	    self._totalSize = self._totalSize + self._size[name]
+
+	# adjust for separators
+	self._totalSize = (self._totalSize +
+                (len(self._paneNames) - 1) * self._separatorThickness)
+
+    def _setrel(self, name):
+	if self._relsize[name] is not None:
+	    if self._majorSize != 0:
+		self._relsize[name] = round(self._size[name]) / self._majorSize
+
+    def _iterate(self, names, proc, n):
+	for i in names:
+	    n = apply(proc, (i, n))
+	    if n == 0:
+		break
+
+    def _grow(self, name, n):
+	canGrow = self._max[name] - self._size[name]
+
+	if canGrow > n:
+	    self._size[name] = self._size[name] + n
+	    self._setrel(name)
+	    return 0
+	elif canGrow > 0:
+	    self._size[name] = self._max[name]
+	    self._setrel(name)
+	    n = n - canGrow
+
+	return n
+
+    def _shrink(self, name, n):
+	canShrink = self._size[name] - self._min[name]
+
+	if canShrink > n:
+	    self._size[name] = self._size[name] - n
+	    self._setrel(name)
+	    return 0
+	elif canShrink > 0:
+	    self._size[name] = self._min[name]
+	    self._setrel(name)
+	    n = n - canShrink
+
+	return n
+
+    def _updateSizes(self):
+	totalSize = 0
+
+	for name in self._paneNames:
+	    size = self._size[name]
+	    if self['orient'] == 'vertical':
+		self._frame[name].place(x = 0, relwidth = 1,
+					y = totalSize,
+					height = size)
+	    else:
+		self._frame[name].place(y = 0, relheight = 1,
+					x = totalSize,
+					width = size)
+
+	    totalSize = totalSize + size + self._separatorThickness
+
+	# Invoke the callback command
+	cmd = self['command']
+	if callable(cmd):
+	    cmd(map(lambda x, s = self: s._size[x], self._paneNames))
+
+    def _plotHandles(self):
+	if len(self._paneNames) == 0:
+	    return
+
+	if self['orient'] == 'vertical':
+	    btnp = self._minorSize - 13
+	else:
+	    h = self._minorSize
+
+	    if h > 18:
+		btnp = 9
+	    else:
+		btnp = h - 9
+
+	firstPane = self._paneNames[0]
+	totalSize = self._size[firstPane]
+
+	first = 1
+	last = len(self._paneNames) - 1
+
+	# loop from first to last, inclusive
+	for i in range(1, last + 1):
+
+	    handlepos = totalSize - 3
+	    prevSize = self._size[self._paneNames[i - 1]]
+	    nextSize = self._size[self._paneNames[i]]
+
+	    offset1 = 0
+
+	    if i == first:
+		if prevSize < 4:
+		    offset1 = 4 - prevSize
+	    else:
+		if prevSize < 8:
+		    offset1 = (8 - prevSize) / 2
+
+	    offset2 = 0
+
+	    if i == last:
+		if nextSize < 4:
+		    offset2 = nextSize - 4
+	    else:
+		if nextSize < 8:
+		    offset2 = (nextSize - 8) / 2
+
+	    handlepos = handlepos + offset1
+
+	    if self['orient'] == 'vertical':
+		height = 8 - offset1 + offset2
+
+		if height > 1:
+		    self._button[i].configure(height = height)
+		    self._button[i].place(x = btnp, y = handlepos)
+		else:
+		    self._button[i].place_forget()
+
+		self._separator[i].place(x = 0, y = totalSize,
+					 relwidth = 1)
+	    else:
+		width = 8 - offset1 + offset2
+
+		if width > 1:
+		    self._button[i].configure(width = width)
+		    self._button[i].place(y = btnp, x = handlepos)
+		else:
+		    self._button[i].place_forget()
+
+		self._separator[i].place(y = 0, x = totalSize,
+					 relheight = 1)
+
+	    totalSize = totalSize + nextSize + self._separatorThickness
+
+    def pane(self, name):
+	return self._frame[self._paneNames[self._nameToIndex(name)]]
+
+    # Return the name of all panes
+    def panes(self):
+	return list(self._paneNames)
+
+    def configurepane(self, name, **kw):
+	name = self._paneNames[self._nameToIndex(name)]
+	self._parsePaneOptions(name, kw)
+	self._handleConfigure()
+
+    def updatelayout(self):
+	self._handleConfigure()
+
+    def _getMotionLimit(self, item):
+	curBefore = (item - 1) * self._separatorThickness
+	minBefore, maxBefore = curBefore, curBefore
+
+	for name in self._paneNames[:item]:
+	    curBefore = curBefore + self._size[name]
+	    minBefore = minBefore + self._min[name]
+	    maxBefore = maxBefore + self._max[name]
+
+	curAfter = (len(self._paneNames) - item) * self._separatorThickness
+	minAfter, maxAfter = curAfter, curAfter
+	for name in self._paneNames[item:]:
+	    curAfter = curAfter + self._size[name]
+	    minAfter = minAfter + self._min[name]
+	    maxAfter = maxAfter + self._max[name]
+
+	beforeToGo = min(curBefore - minBefore, maxAfter - curAfter)
+	afterToGo = min(curAfter - minAfter, maxBefore - curBefore)
+
+	self._beforeLimit = curBefore - beforeToGo
+	self._afterLimit = curBefore + afterToGo
+	self._curSize = curBefore
+
+	self._plotHandles()
+
+    # Compress the motion so that update is quick even on slow machines
+    #
+    # theRootp = root position (either rootx or rooty)
+    def _btnMove(self, event, item):
+	self._rootp = event
+
+	if self._movePending == 0:
+	    self._timerId = self.after_idle(
+		    lambda s = self, i = item: s._btnMoveCompressed(i))
+	    self._movePending = 1
+
+    def destroy(self):
+        if self._timerId is not None:
+          self.after_cancel(self._timerId)
+	  self._timerId = None
+        MegaWidget.destroy(self)
+
+    def _btnMoveCompressed(self, item):
+	if not self._buttonIsDown:
+	    return
+
+	if self['orient'] == 'vertical':
+	    p = self._rootp.y_root - self.winfo_rooty()
+	else:
+	    p = self._rootp.x_root - self.winfo_rootx()
+
+	if p == self._curSize:
+	    self._movePending = 0
+	    return
+
+	if p < self._beforeLimit:
+	    p = self._beforeLimit
+
+	if p >= self._afterLimit:
+	    p = self._afterLimit
+
+	self._calculateChange(item, p)
+	self.update_idletasks()
+	self._movePending = 0
+
+    # Calculate the change in response to mouse motions
+    def _calculateChange(self, item, p):
+
+	if p < self._curSize:
+	    self._moveBefore(item, p)
+	elif p > self._curSize:
+	    self._moveAfter(item, p)
+
+	self._plotHandles()
+
+    def _moveBefore(self, item, p):
+	n = self._curSize - p
+
+	# Shrink the frames before
+	iterRange = list(self._paneNames[:item])
+	iterRange.reverse()
+	self._iterate(iterRange, self._shrink, n)
+
+	# Adjust the frames after
+	iterRange = self._paneNames[item:]
+	self._iterate(iterRange, self._grow, n)
+
+	self._curSize = p
+
+    def _moveAfter(self, item, p):
+	n = p - self._curSize
+
+	# Shrink the frames after
+	iterRange = self._paneNames[item:]
+	self._iterate(iterRange, self._shrink, n)
+
+	# Adjust the frames before
+	iterRange = list(self._paneNames[:item])
+	iterRange.reverse()
+	self._iterate(iterRange, self._grow, n)
+
+	self._curSize = p
+
+######################################################################
+### File: PmwPromptDialog.py
+# Based on iwidgets2.2.0/promptdialog.itk code.
+
+
+
+# A Dialog with an entryfield
+
+class PromptDialog(Dialog):
+    def __init__(self, parent = None, **kw):
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderx',     20,    INITOPT),
+	    ('bordery',     20,    INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	Dialog.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	aliases = (
+	    ('entry', 'entryfield_entry'),
+	    ('label', 'entryfield_label'),
+	)
+	self._promptDialogEntry = self.createcomponent('entryfield',
+		aliases, None,
+		EntryField, (interior,))
+	self._promptDialogEntry.pack(fill='x', expand=1,
+		padx = self['borderx'], pady = self['bordery'])
+	
+        if not kw.has_key('activatecommand'):
+            # Whenever this dialog is activated, set the focus to the
+            # EntryField's entry widget.
+            tkentry = self.component('entry')
+            self.configure(activatecommand = tkentry.focus_set)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    # Supply aliases to some of the entry component methods.
+    def insertentry(self, index, text):
+	self._promptDialogEntry.insert(index, text)
+
+    def deleteentry(self, first, last=None):
+	self._promptDialogEntry.delete(first, last)
+
+    def indexentry(self, index):
+	return self._promptDialogEntry.index(index)
+
+forwardmethods(PromptDialog, EntryField, '_promptDialogEntry')
+
+######################################################################
+### File: PmwRadioSelect.py
+import types
+import Tkinter
+
+
+class RadioSelect(MegaWidget):
+    # A collection of several buttons.  In single mode, only one
+    # button may be selected.  In multiple mode, any number of buttons
+    # may be selected.
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('buttontype',    'button',      INITOPT),
+	    ('command',       None,          None),
+	    ('labelmargin',   0,             INITOPT),
+	    ('labelpos',      None,          INITOPT),
+	    ('orient',       'horizontal',   INITOPT),
+	    ('padx',          5,             INITOPT),
+	    ('pady',          5,             INITOPT),
+	    ('selectmode',    'single',      INITOPT),
+	)
+	self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',))
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	if self['labelpos'] is None:
+	    self._radioSelectFrame = self._hull
+	else:
+	    self._radioSelectFrame = self.createcomponent('frame',
+		    (), None,
+		    Tkinter.Frame, (interior,))
+	    self._radioSelectFrame.grid(column=2, row=2, sticky='nsew')
+	    interior.grid_columnconfigure(2, weight=1)
+	    interior.grid_rowconfigure(2, weight=1)
+
+	    self.createlabel(interior)
+
+	# Initialise instance variables.
+	self._buttonList = []
+	if self['selectmode'] == 'single':
+	    self._singleSelect = 1
+	elif self['selectmode'] == 'multiple':
+	    self._singleSelect = 0
+	else: 
+	    raise ValueError, 'bad selectmode option "' + \
+		    self['selectmode'] + '": should be single or multiple'
+
+	if self['buttontype'] == 'button':
+	    self.buttonClass = Tkinter.Button
+	elif self['buttontype'] == 'radiobutton':
+	    self._singleSelect = 1
+	    self.var = Tkinter.StringVar()
+	    self.buttonClass = Tkinter.Radiobutton
+	elif self['buttontype'] == 'checkbutton':
+	    self._singleSelect = 0
+	    self.buttonClass = Tkinter.Checkbutton
+	else:
+	    raise ValueError, 'bad buttontype option "' + \
+		    self['buttontype'] + \
+		    '": should be button, radiobutton or checkbutton'
+
+	if self._singleSelect:
+	    self.selection = None
+	else:
+	    self.selection = []
+
+	if self['orient'] not in ('horizontal', 'vertical'):
+	    raise ValueError, 'bad orient option ' + repr(self['orient']) + \
+		': must be either \'horizontal\' or \'vertical\''
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def getcurselection(self):
+	if self._singleSelect:
+            return self.selection
+        else:
+            return tuple(self.selection)
+
+    def getvalue(self):
+        return self.getcurselection()
+
+    def setvalue(self, textOrList):
+	if self._singleSelect:
+            self.__setSingleValue(textOrList)
+        else:
+	    # Multiple selections
+            oldselection = self.selection
+            self.selection = textOrList
+            for button in self._buttonList:
+                if button in oldselection:
+                    if button not in self.selection:
+                        # button is currently selected but should not be
+                        widget = self.component(button)
+                        if self['buttontype'] == 'checkbutton':
+                            widget.deselect()
+                        else:  # Button
+                            widget.configure(relief='raised')
+                else:
+                    if button in self.selection:
+                        # button is not currently selected but should be
+                        widget = self.component(button)
+                        if self['buttontype'] == 'checkbutton':
+                            widget.select()
+                        else:  # Button
+                            widget.configure(relief='sunken')
+
+    def numbuttons(self):
+        return len(self._buttonList)
+
+    def index(self, index):
+	# Return the integer index of the button with the given index.
+
+	listLength = len(self._buttonList)
+	if type(index) == types.IntType:
+	    if index < listLength:
+		return index
+	    else:
+		raise ValueError, 'index "%s" is out of range' % index
+	elif index is END:
+	    if listLength > 0:
+		return listLength - 1
+	    else:
+		raise ValueError, 'RadioSelect has no buttons'
+	else:
+	    for count in range(listLength):
+		name = self._buttonList[count]
+		if index == name:
+		    return count
+	    validValues = 'a name, a number or END'
+	    raise ValueError, \
+		    'bad index "%s": must be %s' % (index, validValues)
+
+    def button(self, buttonIndex):
+	name = self._buttonList[self.index(buttonIndex)]
+        return self.component(name)
+
+    def add(self, componentName, **kw):
+	if componentName in self._buttonList:
+	    raise ValueError, 'button "%s" already exists' % componentName
+
+	kw['command'] = \
+                lambda self=self, name=componentName: self.invoke(name)
+	if not kw.has_key('text'):
+	    kw['text'] = componentName
+
+	if self['buttontype'] == 'radiobutton':
+	    if not kw.has_key('anchor'):
+		kw['anchor'] = 'w'
+	    if not kw.has_key('variable'):
+		kw['variable'] = self.var
+	    if not kw.has_key('value'):
+		kw['value'] = kw['text']
+	elif self['buttontype'] == 'checkbutton':
+	    if not kw.has_key('anchor'):
+		kw['anchor'] = 'w'
+
+	button = apply(self.createcomponent, (componentName,
+		(), 'Button',
+		self.buttonClass, (self._radioSelectFrame,)), kw)
+
+	if self['orient'] == 'horizontal':
+	    self._radioSelectFrame.grid_rowconfigure(0, weight=1)
+	    col = len(self._buttonList)
+	    button.grid(column=col, row=0, padx = self['padx'],
+		    pady = self['pady'], sticky='nsew')
+	    self._radioSelectFrame.grid_columnconfigure(col, weight=1)
+	else:
+	    self._radioSelectFrame.grid_columnconfigure(0, weight=1)
+	    row = len(self._buttonList)
+	    button.grid(column=0, row=row, padx = self['padx'],
+		    pady = self['pady'], sticky='ew')
+	    self._radioSelectFrame.grid_rowconfigure(row, weight=1)
+
+	self._buttonList.append(componentName)
+	return button
+
+    def deleteall(self):
+	for name in self._buttonList:
+	    self.destroycomponent(name)
+	self._buttonList = []
+	if self._singleSelect:
+	    self.selection = None
+	else: 
+	    self.selection = []
+
+    def __setSingleValue(self, value):
+            self.selection = value
+            if self['buttontype'] == 'radiobutton':
+                widget = self.component(value)
+                widget.select()
+            else:  # Button
+                for button in self._buttonList:
+                    widget = self.component(button)
+                    if button == value:
+                        widget.configure(relief='sunken')
+                    else:
+                        widget.configure(relief='raised')
+
+    def invoke(self, index):
+	index = self.index(index)
+	name = self._buttonList[index]
+
+	if self._singleSelect:
+            self.__setSingleValue(name)
+	    command = self['command']
+	    if callable(command):
+		return command(name)
+        else:
+	    # Multiple selections
+	    widget = self.component(name)
+	    if name in self.selection:
+		if self['buttontype'] == 'checkbutton':
+		    widget.deselect()
+		else:
+		    widget.configure(relief='raised')
+		self.selection.remove(name)
+		state = 0
+	    else:
+		if self['buttontype'] == 'checkbutton':
+		    widget.select()
+		else:
+		    widget.configure(relief='sunken')
+		self.selection.append(name)
+		state = 1
+
+	    command = self['command']
+	    if callable(command):
+	      return command(name, state)
+
+######################################################################
+### File: PmwScrolledCanvas.py
+import Tkinter
+
+
+class ScrolledCanvas(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderframe',    0,            INITOPT),
+	    ('canvasmargin',   0,            INITOPT),
+	    ('hscrollmode',    'dynamic',    self._hscrollMode),
+	    ('labelmargin',    0,            INITOPT),
+	    ('labelpos',       None,         INITOPT),
+	    ('scrollmargin',   2,            INITOPT),
+	    ('usehullsize',    0,            INITOPT),
+	    ('vscrollmode',    'dynamic',    self._vscrollMode),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	self.origInterior = MegaWidget.interior(self)
+
+	if self['usehullsize']:
+	    self.origInterior.grid_propagate(0)
+
+	if self['borderframe']:
+	    # Create a frame widget to act as the border of the canvas. 
+	    self._borderframe = self.createcomponent('borderframe',
+		    (), None,
+		    Tkinter.Frame, (self.origInterior,),
+		    relief = 'sunken',
+		    borderwidth = 2,
+	    )
+	    self._borderframe.grid(row = 2, column = 2, sticky = 'news')
+
+	    # Create the canvas widget.
+	    self._canvas = self.createcomponent('canvas',
+		    (), None,
+		    Tkinter.Canvas, (self._borderframe,),
+		    highlightthickness = 0,
+		    borderwidth = 0,
+	    )
+	    self._canvas.pack(fill = 'both', expand = 1)
+	else:
+	    # Create the canvas widget.
+	    self._canvas = self.createcomponent('canvas',
+		    (), None,
+		    Tkinter.Canvas, (self.origInterior,),
+		    relief = 'sunken',
+		    borderwidth = 2,
+	    )
+	    self._canvas.grid(row = 2, column = 2, sticky = 'news')
+
+	self.origInterior.grid_rowconfigure(2, weight = 1, minsize = 0)
+	self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
+	
+	# Create the horizontal scrollbar
+	self._horizScrollbar = self.createcomponent('horizscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (self.origInterior,),
+	        orient='horizontal',
+		command=self._canvas.xview
+	)
+
+	# Create the vertical scrollbar
+	self._vertScrollbar = self.createcomponent('vertscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (self.origInterior,),
+		orient='vertical',
+		command=self._canvas.yview
+	)
+
+	self.createlabel(self.origInterior, childCols = 3, childRows = 3)
+
+	# Initialise instance variables.
+	self._horizScrollbarOn = 0
+	self._vertScrollbarOn = 0
+	self.scrollTimer = None
+        self._scrollRecurse = 0
+	self._horizScrollbarNeeded = 0
+	self._vertScrollbarNeeded = 0
+	self.setregionTimer = None
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self.scrollTimer is not None:
+	    self.after_cancel(self.scrollTimer)
+	    self.scrollTimer = None
+	if self.setregionTimer is not None:
+	    self.after_cancel(self.setregionTimer)
+	    self.setregionTimer = None
+	MegaWidget.destroy(self)
+
+    # ======================================================================
+
+    # Public methods.
+
+    def interior(self):
+	return self._canvas
+
+    def resizescrollregion(self):
+	if self.setregionTimer is None:
+	    self.setregionTimer = self.after_idle(self._setRegion)
+
+    # ======================================================================
+
+    # Configuration methods.
+
+    def _hscrollMode(self):
+	# The horizontal scroll mode has been configured.
+
+	mode = self['hscrollmode']
+
+	if mode == 'static':
+	    if not self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'none':
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	else:
+	    message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+        self._configureScrollCommands()
+
+    def _vscrollMode(self):
+	# The vertical scroll mode has been configured.
+
+	mode = self['vscrollmode']
+
+	if mode == 'static':
+	    if not self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'none':
+	    if self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	else:
+	    message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+        self._configureScrollCommands()
+
+    # ======================================================================
+
+    # Private methods.
+
+    def _configureScrollCommands(self):
+        # If both scrollmodes are not dynamic we can save a lot of
+        # time by not having to create an idle job to handle the
+        # scroll commands.
+
+        # Clean up previous scroll commands to prevent memory leak.
+        tclCommandName = str(self._canvas.cget('xscrollcommand'))
+        if tclCommandName != '':   
+            self._canvas.deletecommand(tclCommandName)
+        tclCommandName = str(self._canvas.cget('yscrollcommand'))
+        if tclCommandName != '':   
+            self._canvas.deletecommand(tclCommandName)
+
+	if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
+            self._canvas.configure(
+                    xscrollcommand=self._scrollBothLater,
+                    yscrollcommand=self._scrollBothLater
+            )
+        else:
+            self._canvas.configure(
+                    xscrollcommand=self._scrollXNow,
+                    yscrollcommand=self._scrollYNow
+            )
+
+    def _scrollXNow(self, first, last):
+        self._horizScrollbar.set(first, last)
+        self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
+
+	if self['hscrollmode'] == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+
+    def _scrollYNow(self, first, last):
+        self._vertScrollbar.set(first, last)
+        self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
+
+        if self['vscrollmode'] == 'dynamic':
+            if self._vertScrollbarNeeded != self._vertScrollbarOn:
+                self._toggleVertScrollbar()
+
+    def _scrollBothLater(self, first, last):
+	# Called by the canvas to set the horizontal or vertical
+	# scrollbar when it has scrolled or changed scrollregion.
+
+	if self.scrollTimer is None:
+	    self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+    def _scrollBothNow(self):
+        # This performs the function of _scrollXNow and _scrollYNow.
+        # If one is changed, the other should be updated to match.
+	self.scrollTimer = None
+
+        # Call update_idletasks to make sure that the containing frame
+        # has been resized before we attempt to set the scrollbars. 
+        # Otherwise the scrollbars may be mapped/unmapped continuously.
+        self._scrollRecurse = self._scrollRecurse + 1
+        self.update_idletasks()
+        self._scrollRecurse = self._scrollRecurse - 1
+        if self._scrollRecurse != 0:
+            return
+
+	xview = self._canvas.xview()
+	yview = self._canvas.yview()
+	self._horizScrollbar.set(xview[0], xview[1])
+	self._vertScrollbar.set(yview[0], yview[1])
+
+	self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+	self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+	# If both horizontal and vertical scrollmodes are dynamic and
+	# currently only one scrollbar is mapped and both should be
+	# toggled, then unmap the mapped scrollbar.  This prevents a
+	# continuous mapping and unmapping of the scrollbars. 
+	if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+		self._horizScrollbarNeeded != self._horizScrollbarOn and
+		self._vertScrollbarNeeded != self._vertScrollbarOn and
+		self._vertScrollbarOn != self._horizScrollbarOn):
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	    else:
+		self._toggleVertScrollbar()
+	    return
+
+	if self['hscrollmode'] == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+
+	if self['vscrollmode'] == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+
+    def _toggleHorizScrollbar(self):
+
+	self._horizScrollbarOn = not self._horizScrollbarOn
+
+	interior = self.origInterior
+	if self._horizScrollbarOn:
+	    self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
+	    interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
+	else:
+	    self._horizScrollbar.grid_forget()
+	    interior.grid_rowconfigure(3, minsize = 0)
+
+    def _toggleVertScrollbar(self):
+
+	self._vertScrollbarOn = not self._vertScrollbarOn
+
+	interior = self.origInterior
+	if self._vertScrollbarOn:
+	    self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
+	    interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+	else:
+	    self._vertScrollbar.grid_forget()
+	    interior.grid_columnconfigure(3, minsize = 0)
+
+    def _setRegion(self):
+	self.setregionTimer = None
+
+	region = self._canvas.bbox('all')
+        if region is not None:
+	    canvasmargin = self['canvasmargin']
+	    region = (region[0] - canvasmargin, region[1] - canvasmargin,
+		region[2] + canvasmargin, region[3] + canvasmargin)
+	    self._canvas.configure(scrollregion = region)
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+    def bbox(self, *args):
+	return apply(self._canvas.bbox, args)
+
+forwardmethods(ScrolledCanvas, Tkinter.Canvas, '_canvas')
+
+######################################################################
+### File: PmwScrolledField.py
+import Tkinter
+
+
+class ScrolledField(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('labelmargin',   0,      INITOPT),
+	    ('labelpos',      None,   INITOPT),
+	    ('sticky',        'ew',   INITOPT),
+	    ('text',          '',     self._text),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	self._scrolledFieldEntry = self.createcomponent('entry',
+		(), None,
+		Tkinter.Entry, (interior,))
+
+        # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+        try:
+            self._scrolledFieldEntry.configure(state = 'readonly')
+        except Tkinter.TclError:
+            self._scrolledFieldEntry.configure(state = 'disabled')
+
+	self._scrolledFieldEntry.grid(column=2, row=2, sticky=self['sticky'])
+	interior.grid_columnconfigure(2, weight=1)
+	interior.grid_rowconfigure(2, weight=1)
+
+	self.createlabel(interior)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def _text(self):
+        text = self['text']
+        self._scrolledFieldEntry.configure(state = 'normal')
+        self._scrolledFieldEntry.delete(0, 'end')
+        self._scrolledFieldEntry.insert('end', text)
+
+        # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+        try:
+            self._scrolledFieldEntry.configure(state = 'readonly')
+        except Tkinter.TclError:
+            self._scrolledFieldEntry.configure(state = 'disabled')
+
+forwardmethods(ScrolledField, Tkinter.Entry, '_scrolledFieldEntry')
+
+######################################################################
+### File: PmwScrolledFrame.py
+import string
+import types
+import Tkinter
+
+
+class ScrolledFrame(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderframe',    1,            INITOPT),
+	    ('horizflex',      'fixed',      self._horizflex),
+	    ('horizfraction',  0.05,         INITOPT),
+	    ('hscrollmode',    'dynamic',    self._hscrollMode),
+	    ('labelmargin',    0,            INITOPT),
+	    ('labelpos',       None,         INITOPT),
+	    ('scrollmargin',   2,            INITOPT),
+	    ('usehullsize',    0,            INITOPT),
+	    ('vertflex',       'fixed',      self._vertflex),
+	    ('vertfraction',   0.05,         INITOPT),
+	    ('vscrollmode',    'dynamic',    self._vscrollMode),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	self.origInterior = MegaWidget.interior(self)
+
+	if self['usehullsize']:
+	    self.origInterior.grid_propagate(0)
+
+	if self['borderframe']:
+	    # Create a frame widget to act as the border of the clipper. 
+	    self._borderframe = self.createcomponent('borderframe',
+		    (), None,
+		    Tkinter.Frame, (self.origInterior,),
+		    relief = 'sunken',
+		    borderwidth = 2,
+	    )
+	    self._borderframe.grid(row = 2, column = 2, sticky = 'news')
+
+	    # Create the clipping window.
+	    self._clipper = self.createcomponent('clipper',
+		    (), None,
+		    Tkinter.Frame, (self._borderframe,),
+		    width = 400,
+		    height = 300,
+		    highlightthickness = 0,
+		    borderwidth = 0,
+	    )
+	    self._clipper.pack(fill = 'both', expand = 1)
+	else:
+	    # Create the clipping window.
+	    self._clipper = self.createcomponent('clipper',
+		    (), None,
+		    Tkinter.Frame, (self.origInterior,),
+		    width = 400,
+		    height = 300,
+		    relief = 'sunken',
+		    borderwidth = 2,
+	    )
+	    self._clipper.grid(row = 2, column = 2, sticky = 'news')
+
+	self.origInterior.grid_rowconfigure(2, weight = 1, minsize = 0)
+	self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
+	
+	# Create the horizontal scrollbar
+	self._horizScrollbar = self.createcomponent('horizscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (self.origInterior,),
+	        orient='horizontal',
+		command=self.xview
+	)
+
+	# Create the vertical scrollbar
+	self._vertScrollbar = self.createcomponent('vertscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (self.origInterior,),
+		orient='vertical',
+		command=self.yview
+	)
+
+	self.createlabel(self.origInterior, childCols = 3, childRows = 3)
+
+	# Initialise instance variables.
+	self._horizScrollbarOn = 0
+	self._vertScrollbarOn = 0
+	self.scrollTimer = None
+	self._scrollRecurse = 0
+	self._horizScrollbarNeeded = 0
+	self._vertScrollbarNeeded = 0
+	self.startX = 0
+	self.startY = 0
+	self._flexoptions = ('fixed', 'expand', 'shrink', 'elastic')
+
+	# Create a frame in the clipper to contain the widgets to be
+	# scrolled.
+	self._frame = self.createcomponent('frame',
+		(), None,
+		Tkinter.Frame, (self._clipper,)
+	)
+
+	# Whenever the clipping window or scrolled frame change size,
+	# update the scrollbars.
+	self._frame.bind('<Configure>', self._reposition)
+	self._clipper.bind('<Configure>', self._reposition)
+
+        # Work around a bug in Tk where the value returned by the
+        # scrollbar get() method is (0.0, 0.0, 0.0, 0.0) rather than
+        # the expected 2-tuple.  This occurs if xview() is called soon
+        # after the ScrolledFrame has been created.
+        self._horizScrollbar.set(0.0, 1.0)
+        self._vertScrollbar.set(0.0, 1.0)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self.scrollTimer is not None:
+	    self.after_cancel(self.scrollTimer)
+	    self.scrollTimer = None
+	MegaWidget.destroy(self)
+
+    # ======================================================================
+
+    # Public methods.
+
+    def interior(self):
+	return self._frame
+
+    # Set timer to call real reposition method, so that it is not
+    # called multiple times when many things are reconfigured at the
+    # same time.
+    def reposition(self):
+	if self.scrollTimer is None:
+	    self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+    # Called when the user clicks in the horizontal scrollbar. 
+    # Calculates new position of frame then calls reposition() to
+    # update the frame and the scrollbar.
+    def xview(self, mode = None, value = None, units = None):
+
+        if type(value) == types.StringType:
+            value = string.atof(value)
+        if mode is None:
+            return self._horizScrollbar.get()
+	elif mode == 'moveto':
+	    frameWidth = self._frame.winfo_reqwidth()
+	    self.startX = value * float(frameWidth)
+	else: # mode == 'scroll'
+	    clipperWidth = self._clipper.winfo_width()
+	    if units == 'units':
+		jump = int(clipperWidth * self['horizfraction'])
+	    else:
+		jump = clipperWidth
+            self.startX = self.startX + value * jump
+
+	self.reposition()
+
+    # Called when the user clicks in the vertical scrollbar. 
+    # Calculates new position of frame then calls reposition() to
+    # update the frame and the scrollbar.
+    def yview(self, mode = None, value = None, units = None):
+
+        if type(value) == types.StringType:
+            value = string.atof(value)
+        if mode is None:
+            return self._vertScrollbar.get()
+	elif mode == 'moveto':
+	    frameHeight = self._frame.winfo_reqheight()
+	    self.startY = value * float(frameHeight)
+	else: # mode == 'scroll'
+	    clipperHeight = self._clipper.winfo_height()
+	    if units == 'units':
+		jump = int(clipperHeight * self['vertfraction'])
+	    else:
+		jump = clipperHeight
+            self.startY = self.startY + value * jump
+
+	self.reposition()
+
+    # ======================================================================
+
+    # Configuration methods.
+
+    def _hscrollMode(self):
+	# The horizontal scroll mode has been configured.
+
+	mode = self['hscrollmode']
+
+	if mode == 'static':
+	    if not self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'none':
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	else:
+	    message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+    def _vscrollMode(self):
+	# The vertical scroll mode has been configured.
+
+	mode = self['vscrollmode']
+
+	if mode == 'static':
+	    if not self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'none':
+	    if self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	else:
+	    message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+    def _horizflex(self):
+	# The horizontal flex mode has been configured.
+
+	flex = self['horizflex']
+
+	if flex not in self._flexoptions:
+	    message = 'bad horizflex option "%s": should be one of %s' % \
+		    (flex, str(self._flexoptions))
+	    raise ValueError, message
+
+	self.reposition()
+
+    def _vertflex(self):
+	# The vertical flex mode has been configured.
+
+	flex = self['vertflex']
+
+	if flex not in self._flexoptions:
+	    message = 'bad vertflex option "%s": should be one of %s' % \
+		    (flex, str(self._flexoptions))
+	    raise ValueError, message
+
+	self.reposition()
+
+    # ======================================================================
+
+    # Private methods.
+
+    def _reposition(self, event):
+	self.reposition()
+
+    def _getxview(self):
+
+	# Horizontal dimension.
+	clipperWidth = self._clipper.winfo_width()
+	frameWidth = self._frame.winfo_reqwidth()
+	if frameWidth <= clipperWidth:
+	    # The scrolled frame is smaller than the clipping window.
+
+	    self.startX = 0
+	    endScrollX = 1.0
+
+	    if self['horizflex'] in ('expand', 'elastic'):
+		relwidth = 1
+	    else:
+		relwidth = ''
+	else:
+	    # The scrolled frame is larger than the clipping window.
+
+	    if self['horizflex'] in ('shrink', 'elastic'):
+		self.startX = 0
+		endScrollX = 1.0
+		relwidth = 1
+	    else:
+		if self.startX + clipperWidth > frameWidth:
+		    self.startX = frameWidth - clipperWidth
+		    endScrollX = 1.0
+		else:
+		    if self.startX < 0:
+			self.startX = 0
+		    endScrollX = (self.startX + clipperWidth) / float(frameWidth)
+		relwidth = ''
+
+	# Position frame relative to clipper.
+	self._frame.place(x = -self.startX, relwidth = relwidth)
+	return (self.startX / float(frameWidth), endScrollX)
+
+    def _getyview(self):
+
+	# Vertical dimension.
+	clipperHeight = self._clipper.winfo_height()
+	frameHeight = self._frame.winfo_reqheight()
+	if frameHeight <= clipperHeight:
+	    # The scrolled frame is smaller than the clipping window.
+
+	    self.startY = 0
+	    endScrollY = 1.0
+
+	    if self['vertflex'] in ('expand', 'elastic'):
+		relheight = 1
+	    else:
+		relheight = ''
+	else:
+	    # The scrolled frame is larger than the clipping window.
+
+	    if self['vertflex'] in ('shrink', 'elastic'):
+		self.startY = 0
+		endScrollY = 1.0
+		relheight = 1
+	    else:
+		if self.startY + clipperHeight > frameHeight:
+		    self.startY = frameHeight - clipperHeight
+		    endScrollY = 1.0
+		else:
+		    if self.startY < 0:
+			self.startY = 0
+		    endScrollY = (self.startY + clipperHeight) / float(frameHeight)
+		relheight = ''
+
+	# Position frame relative to clipper.
+	self._frame.place(y = -self.startY, relheight = relheight)
+	return (self.startY / float(frameHeight), endScrollY)
+
+    # According to the relative geometries of the frame and the
+    # clipper, reposition the frame within the clipper and reset the
+    # scrollbars.
+    def _scrollBothNow(self):
+	self.scrollTimer = None
+
+        # Call update_idletasks to make sure that the containing frame
+        # has been resized before we attempt to set the scrollbars. 
+        # Otherwise the scrollbars may be mapped/unmapped continuously.
+        self._scrollRecurse = self._scrollRecurse + 1
+        self.update_idletasks()
+        self._scrollRecurse = self._scrollRecurse - 1
+        if self._scrollRecurse != 0:
+            return
+
+	xview = self._getxview()
+	yview = self._getyview()
+	self._horizScrollbar.set(xview[0], xview[1])
+	self._vertScrollbar.set(yview[0], yview[1])
+
+	self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+	self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+	# If both horizontal and vertical scrollmodes are dynamic and
+	# currently only one scrollbar is mapped and both should be
+	# toggled, then unmap the mapped scrollbar.  This prevents a
+	# continuous mapping and unmapping of the scrollbars. 
+	if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+		self._horizScrollbarNeeded != self._horizScrollbarOn and
+		self._vertScrollbarNeeded != self._vertScrollbarOn and
+		self._vertScrollbarOn != self._horizScrollbarOn):
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	    else:
+		self._toggleVertScrollbar()
+	    return
+
+	if self['hscrollmode'] == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+
+	if self['vscrollmode'] == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+
+    def _toggleHorizScrollbar(self):
+
+	self._horizScrollbarOn = not self._horizScrollbarOn
+
+	interior = self.origInterior
+	if self._horizScrollbarOn:
+	    self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
+	    interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
+	else:
+	    self._horizScrollbar.grid_forget()
+	    interior.grid_rowconfigure(3, minsize = 0)
+
+    def _toggleVertScrollbar(self):
+
+	self._vertScrollbarOn = not self._vertScrollbarOn
+
+	interior = self.origInterior
+	if self._vertScrollbarOn:
+	    self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
+	    interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+	else:
+	    self._vertScrollbar.grid_forget()
+	    interior.grid_columnconfigure(3, minsize = 0)
+
+######################################################################
+### File: PmwScrolledListBox.py
+# Based on iwidgets2.2.0/scrolledlistbox.itk code.
+
+import types
+import Tkinter
+
+
+class ScrolledListBox(MegaWidget):
+    _classBindingsDefinedFor = 0
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('dblclickcommand',    None,            None),
+	    ('hscrollmode',        'dynamic',       self._hscrollMode),
+	    ('items',              (),              INITOPT),
+	    ('labelmargin',        0,               INITOPT),
+	    ('labelpos',           None,            INITOPT),
+	    ('scrollmargin',       2,               INITOPT),
+	    ('selectioncommand',   None,            None),
+	    ('usehullsize',        0,               INITOPT),
+	    ('vscrollmode',        'dynamic',       self._vscrollMode),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	if self['usehullsize']:
+	    interior.grid_propagate(0)
+
+	# Create the listbox widget.
+	self._listbox = self.createcomponent('listbox',
+		(), None,
+		Tkinter.Listbox, (interior,))
+	self._listbox.grid(row = 2, column = 2, sticky = 'news')
+	interior.grid_rowconfigure(2, weight = 1, minsize = 0)
+	interior.grid_columnconfigure(2, weight = 1, minsize = 0)
+
+	# Create the horizontal scrollbar
+	self._horizScrollbar = self.createcomponent('horizscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (interior,),
+	        orient='horizontal',
+		command=self._listbox.xview
+	)
+
+	# Create the vertical scrollbar
+	self._vertScrollbar = self.createcomponent('vertscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (interior,),
+		orient='vertical',
+		command=self._listbox.yview
+	)
+
+	self.createlabel(interior, childCols = 3, childRows = 3)
+
+	# Add the items specified by the initialisation option.
+	items = self['items']
+	if type(items) != types.TupleType:
+	    items = tuple(items)
+	if len(items) > 0:
+	    apply(self._listbox.insert, ('end',) + items)
+
+	_registerScrolledList(self._listbox, self)
+
+        # Establish the special class bindings if not already done.
+        # Also create bindings if the Tkinter default interpreter has
+        # changed.  Use Tkinter._default_root to create class
+        # bindings, so that a reference to root is created by
+        # bind_class rather than a reference to self, which would
+        # prevent object cleanup.
+        theTag = 'ScrolledListBoxTag'
+        if ScrolledListBox._classBindingsDefinedFor != Tkinter._default_root:
+            root  = Tkinter._default_root
+	    	    
+            def doubleEvent(event):
+                _handleEvent(event, 'double')
+            def keyEvent(event):
+                _handleEvent(event, 'key')
+            def releaseEvent(event):
+                _handleEvent(event, 'release')
+
+            # Bind space and return keys and button 1 to the selectioncommand.
+            root.bind_class(theTag, '<Key-space>', keyEvent)
+            root.bind_class(theTag, '<Key-Return>', keyEvent)
+            root.bind_class(theTag, '<ButtonRelease-1>', releaseEvent)
+
+            # Bind double button 1 click to the dblclickcommand.
+            root.bind_class(theTag, '<Double-ButtonRelease-1>', doubleEvent)
+
+	    ScrolledListBox._classBindingsDefinedFor = root
+
+	bindtags = self._listbox.bindtags()
+	self._listbox.bindtags(bindtags + (theTag,))
+
+	# Initialise instance variables.
+	self._horizScrollbarOn = 0
+	self._vertScrollbarOn = 0
+	self.scrollTimer = None
+        self._scrollRecurse = 0
+	self._horizScrollbarNeeded = 0
+	self._vertScrollbarNeeded = 0
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self.scrollTimer is not None:
+	    self.after_cancel(self.scrollTimer)
+	    self.scrollTimer = None
+	_deregisterScrolledList(self._listbox)
+	MegaWidget.destroy(self)
+
+    # ======================================================================
+
+    # Public methods.
+
+    def clear(self):
+	self.setlist(())
+
+    def getcurselection(self):
+	rtn = []
+	for sel in self.curselection():
+	    rtn.append(self._listbox.get(sel))
+	return tuple(rtn)
+
+    def getvalue(self):
+        return self.getcurselection()
+
+    def setvalue(self, textOrList):
+        self._listbox.selection_clear(0, 'end')
+        listitems = list(self._listbox.get(0, 'end'))
+        if type(textOrList) == types.StringType:
+            if textOrList in listitems:
+                self._listbox.selection_set(listitems.index(textOrList))
+            else:
+                raise ValueError, 'no such item "%s"' % textOrList
+        else:
+            for item in textOrList:
+                if item in listitems:
+                    self._listbox.selection_set(listitems.index(item))
+                else:
+                    raise ValueError, 'no such item "%s"' % item
+
+    def setlist(self, items):
+        self._listbox.delete(0, 'end')
+	if len(items) > 0:
+	    if type(items) != types.TupleType:
+		items = tuple(items)
+	    apply(self._listbox.insert, (0,) + items)
+
+    # Override Tkinter.Listbox get method, so that if it is called with
+    # no arguments, return all list elements (consistent with other widgets).
+    def get(self, first=None, last=None):
+	if first is None:
+	    return self._listbox.get(0, 'end')
+	else:
+	    return self._listbox.get(first, last)
+
+    # ======================================================================
+
+    # Configuration methods.
+
+    def _hscrollMode(self):
+	# The horizontal scroll mode has been configured.
+
+	mode = self['hscrollmode']
+
+	if mode == 'static':
+	    if not self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'none':
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	else:
+	    message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+        self._configureScrollCommands()
+
+    def _vscrollMode(self):
+	# The vertical scroll mode has been configured.
+
+	mode = self['vscrollmode']
+
+	if mode == 'static':
+	    if not self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'none':
+	    if self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	else:
+	    message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+        self._configureScrollCommands()
+
+    # ======================================================================
+
+    # Private methods.
+
+    def _configureScrollCommands(self):
+        # If both scrollmodes are not dynamic we can save a lot of
+        # time by not having to create an idle job to handle the
+        # scroll commands.
+
+        # Clean up previous scroll commands to prevent memory leak.
+        tclCommandName = str(self._listbox.cget('xscrollcommand'))
+        if tclCommandName != '':   
+            self._listbox.deletecommand(tclCommandName)
+        tclCommandName = str(self._listbox.cget('yscrollcommand'))
+        if tclCommandName != '':   
+            self._listbox.deletecommand(tclCommandName)
+
+	if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
+            self._listbox.configure(
+                    xscrollcommand=self._scrollBothLater,
+                    yscrollcommand=self._scrollBothLater
+            )
+        else:
+            self._listbox.configure(
+                    xscrollcommand=self._scrollXNow,
+                    yscrollcommand=self._scrollYNow
+            )
+
+    def _scrollXNow(self, first, last):
+        self._horizScrollbar.set(first, last)
+        self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
+
+	if self['hscrollmode'] == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+
+    def _scrollYNow(self, first, last):
+        self._vertScrollbar.set(first, last)
+        self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
+
+        if self['vscrollmode'] == 'dynamic':
+            if self._vertScrollbarNeeded != self._vertScrollbarOn:
+                self._toggleVertScrollbar()
+
+    def _scrollBothLater(self, first, last):
+	# Called by the listbox to set the horizontal or vertical
+	# scrollbar when it has scrolled or changed size or contents.
+
+	if self.scrollTimer is None:
+	    self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+    def _scrollBothNow(self):
+        # This performs the function of _scrollXNow and _scrollYNow.
+        # If one is changed, the other should be updated to match.
+	self.scrollTimer = None
+
+        # Call update_idletasks to make sure that the containing frame
+        # has been resized before we attempt to set the scrollbars. 
+        # Otherwise the scrollbars may be mapped/unmapped continuously.
+        self._scrollRecurse = self._scrollRecurse + 1
+        self.update_idletasks()
+        self._scrollRecurse = self._scrollRecurse - 1
+        if self._scrollRecurse != 0:
+            return
+
+	xview = self._listbox.xview()
+	yview = self._listbox.yview()
+	self._horizScrollbar.set(xview[0], xview[1])
+	self._vertScrollbar.set(yview[0], yview[1])
+
+	self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+	self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+	# If both horizontal and vertical scrollmodes are dynamic and
+	# currently only one scrollbar is mapped and both should be
+	# toggled, then unmap the mapped scrollbar.  This prevents a
+	# continuous mapping and unmapping of the scrollbars. 
+	if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+		self._horizScrollbarNeeded != self._horizScrollbarOn and
+		self._vertScrollbarNeeded != self._vertScrollbarOn and
+		self._vertScrollbarOn != self._horizScrollbarOn):
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	    else:
+		self._toggleVertScrollbar()
+	    return
+
+	if self['hscrollmode'] == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+
+	if self['vscrollmode'] == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+
+    def _toggleHorizScrollbar(self):
+
+	self._horizScrollbarOn = not self._horizScrollbarOn
+
+	interior = self.interior()
+	if self._horizScrollbarOn:
+	    self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
+	    interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
+	else:
+	    self._horizScrollbar.grid_forget()
+	    interior.grid_rowconfigure(3, minsize = 0)
+
+    def _toggleVertScrollbar(self):
+
+	self._vertScrollbarOn = not self._vertScrollbarOn
+
+	interior = self.interior()
+	if self._vertScrollbarOn:
+	    self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
+	    interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+	else:
+	    self._vertScrollbar.grid_forget()
+	    interior.grid_columnconfigure(3, minsize = 0)
+
+    def _handleEvent(self, event, eventType):
+        if eventType == 'double':
+            command = self['dblclickcommand']
+        elif eventType == 'key':
+            command = self['selectioncommand']
+        else: #eventType == 'release'
+            # Do not execute the command if the mouse was released
+            # outside the listbox.
+            if (event.x < 0 or self._listbox.winfo_width() <= event.x or
+                    event.y < 0 or self._listbox.winfo_height() <= event.y):
+                return
+
+            command = self['selectioncommand']
+
+        if callable(command):
+            command()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)size method inherited from Tkinter.Frame.Grid.
+    def size(self):
+	return self._listbox.size()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+    def bbox(self, index):
+	return self._listbox.bbox(index)
+
+forwardmethods(ScrolledListBox, Tkinter.Listbox, '_listbox')
+
+# ======================================================================
+
+_listboxCache = {}
+
+def _registerScrolledList(listbox, scrolledList):
+    # Register an ScrolledList widget for a Listbox widget
+
+    _listboxCache[listbox] = scrolledList
+
+def _deregisterScrolledList(listbox):
+    # Deregister a Listbox widget
+    del _listboxCache[listbox]
+
+def _handleEvent(event, eventType):
+    # Forward events for a Listbox to it's ScrolledListBox
+
+    # A binding earlier in the bindtags list may have destroyed the
+    # megawidget, so need to check.
+    if _listboxCache.has_key(event.widget):
+        _listboxCache[event.widget]._handleEvent(event, eventType)
+
+######################################################################
+### File: PmwScrolledText.py
+# Based on iwidgets2.2.0/scrolledtext.itk code.   
+
+import Tkinter
+
+
+class ScrolledText(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderframe',    0,            INITOPT),
+	    ('columnheader',   0,            INITOPT),
+	    ('hscrollmode',    'dynamic',    self._hscrollMode),
+	    ('labelmargin',    0,            INITOPT),
+	    ('labelpos',       None,         INITOPT),
+	    ('rowcolumnheader',0,            INITOPT),
+	    ('rowheader',      0,            INITOPT),
+	    ('scrollmargin',   2,            INITOPT),
+	    ('usehullsize',    0,            INITOPT),
+	    ('vscrollmode',    'dynamic',    self._vscrollMode),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	if self['usehullsize']:
+	    interior.grid_propagate(0)
+
+	if self['borderframe']:
+	    # Create a frame widget to act as the border of the text 
+	    # widget.  Later, pack the text widget so that it fills
+	    # the frame.  This avoids a problem in Tk, where window
+	    # items in a text widget may overlap the border of the
+	    # text widget.
+	    self._borderframe = self.createcomponent('borderframe',
+		    (), None,
+		    Tkinter.Frame, (interior,),
+		    relief = 'sunken',
+		    borderwidth = 2,
+	    )
+	    self._borderframe.grid(row = 4, column = 4, sticky = 'news')
+
+	    # Create the text widget.
+	    self._textbox = self.createcomponent('text',
+		    (), None,
+		    Tkinter.Text, (self._borderframe,),
+		    highlightthickness = 0,
+		    borderwidth = 0,
+	    )
+	    self._textbox.pack(fill = 'both', expand = 1)
+
+            bw = self._borderframe.cget('borderwidth'),
+            ht = self._borderframe.cget('highlightthickness'),
+	else:
+	    # Create the text widget.
+	    self._textbox = self.createcomponent('text',
+		    (), None,
+		    Tkinter.Text, (interior,),
+	    )
+	    self._textbox.grid(row = 4, column = 4, sticky = 'news')
+
+            bw = self._textbox.cget('borderwidth'),
+            ht = self._textbox.cget('highlightthickness'),
+
+        # Create the header text widgets
+        if self['columnheader']:
+            self._columnheader = self.createcomponent('columnheader',
+                    (), 'Header',
+                    Tkinter.Text, (interior,),
+                    height=1,
+                    wrap='none',
+                    borderwidth = bw,
+                    highlightthickness = ht,
+            )
+            self._columnheader.grid(row = 2, column = 4, sticky = 'ew')
+            self._columnheader.configure(
+                    xscrollcommand = self._columnheaderscrolled)
+
+        if self['rowheader']:
+            self._rowheader = self.createcomponent('rowheader',
+                    (), 'Header',
+                    Tkinter.Text, (interior,),
+                    wrap='none',
+                    borderwidth = bw,
+                    highlightthickness = ht,
+            )
+            self._rowheader.grid(row = 4, column = 2, sticky = 'ns')
+            self._rowheader.configure(
+                    yscrollcommand = self._rowheaderscrolled)
+
+        if self['rowcolumnheader']:
+            self._rowcolumnheader = self.createcomponent('rowcolumnheader',
+                    (), 'Header',
+                    Tkinter.Text, (interior,),
+                    height=1,
+                    wrap='none',
+                    borderwidth = bw,
+                    highlightthickness = ht,
+            )
+            self._rowcolumnheader.grid(row = 2, column = 2, sticky = 'nsew')
+
+	interior.grid_rowconfigure(4, weight = 1, minsize = 0)
+	interior.grid_columnconfigure(4, weight = 1, minsize = 0)
+
+	# Create the horizontal scrollbar
+	self._horizScrollbar = self.createcomponent('horizscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (interior,),
+	        orient='horizontal',
+		command=self._textbox.xview
+	)
+
+	# Create the vertical scrollbar
+	self._vertScrollbar = self.createcomponent('vertscrollbar',
+		(), 'Scrollbar',
+		Tkinter.Scrollbar, (interior,),
+		orient='vertical',
+		command=self._textbox.yview
+	)
+
+	self.createlabel(interior, childCols = 5, childRows = 5)
+
+	# Initialise instance variables.
+	self._horizScrollbarOn = 0
+	self._vertScrollbarOn = 0
+	self.scrollTimer = None
+        self._scrollRecurse = 0
+	self._horizScrollbarNeeded = 0
+	self._vertScrollbarNeeded = 0
+	self._textWidth = None
+
+        # These four variables avoid an infinite loop caused by the
+        # row or column header's scrollcommand causing the main text
+        # widget's scrollcommand to be called and vice versa.
+	self._textboxLastX = None
+	self._textboxLastY = None
+	self._columnheaderLastX = None
+	self._rowheaderLastY = None
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self.scrollTimer is not None:
+	    self.after_cancel(self.scrollTimer)
+	    self.scrollTimer = None
+	MegaWidget.destroy(self)
+
+    # ======================================================================
+
+    # Public methods.
+
+    def clear(self):
+	self.settext('')
+
+    def importfile(self, fileName, where = 'end'):
+	file = open(fileName, 'r')
+	self._textbox.insert(where, file.read())
+	file.close()
+
+    def exportfile(self, fileName):
+	file = open(fileName, 'w')
+	file.write(self._textbox.get('1.0', 'end'))
+	file.close()
+
+    def settext(self, text):
+	disabled = (str(self._textbox.cget('state')) == 'disabled')
+	if disabled:
+	    self._textbox.configure(state='normal')
+	self._textbox.delete('0.0', 'end')
+	self._textbox.insert('end', text)
+	if disabled:
+	    self._textbox.configure(state='disabled')
+
+    # Override Tkinter.Text get method, so that if it is called with
+    # no arguments, return all text (consistent with other widgets).
+    def get(self, first=None, last=None):
+	if first is None:
+	    return self._textbox.get('1.0', 'end')
+	else:
+	    return self._textbox.get(first, last)
+
+    def getvalue(self):
+        return self.get()
+
+    def setvalue(self, text):
+        return self.settext(text)
+
+    def appendtext(self, text):
+        oldTop, oldBottom = self._textbox.yview()
+     
+        disabled = (str(self._textbox.cget('state')) == 'disabled')
+        if disabled:
+            self._textbox.configure(state='normal')
+        self._textbox.insert('end', text)
+        if disabled:
+            self._textbox.configure(state='disabled')
+     
+        if oldBottom == 1.0:
+            self._textbox.yview('moveto', 1.0)
+
+    # ======================================================================
+
+    # Configuration methods.
+
+    def _hscrollMode(self):
+	# The horizontal scroll mode has been configured.
+
+	mode = self['hscrollmode']
+
+	if mode == 'static':
+	    if not self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'dynamic':
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	elif mode == 'none':
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	else:
+	    message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+        self._configureScrollCommands()
+
+    def _vscrollMode(self):
+	# The vertical scroll mode has been configured.
+
+	mode = self['vscrollmode']
+
+	if mode == 'static':
+	    if not self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	elif mode == 'none':
+	    if self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+	else:
+	    message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+	    raise ValueError, message
+
+        self._configureScrollCommands()
+
+    # ======================================================================
+
+    # Private methods.
+
+    def _configureScrollCommands(self):
+        # If both scrollmodes are not dynamic we can save a lot of
+        # time by not having to create an idle job to handle the
+        # scroll commands.
+
+        # Clean up previous scroll commands to prevent memory leak.
+        tclCommandName = str(self._textbox.cget('xscrollcommand'))
+        if tclCommandName != '':   
+            self._textbox.deletecommand(tclCommandName)
+        tclCommandName = str(self._textbox.cget('yscrollcommand'))
+        if tclCommandName != '':   
+            self._textbox.deletecommand(tclCommandName)
+
+	if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
+            self._textbox.configure(
+                    xscrollcommand=self._scrollBothLater,
+                    yscrollcommand=self._scrollBothLater
+            )
+        else:
+            self._textbox.configure(
+                    xscrollcommand=self._scrollXNow,
+                    yscrollcommand=self._scrollYNow
+            )
+
+    def _scrollXNow(self, first, last):
+        self._horizScrollbar.set(first, last)
+        self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
+
+        # This code is the same as in _scrollBothNow.  Keep it that way.
+        if self['hscrollmode'] == 'dynamic':
+            currentWidth = self._textbox.winfo_width()
+            if self._horizScrollbarNeeded != self._horizScrollbarOn:
+                if self._horizScrollbarNeeded or \
+                        self._textWidth != currentWidth:
+                    self._toggleHorizScrollbar()
+            self._textWidth = currentWidth
+
+        if self['columnheader']:
+	    if self._columnheaderLastX != first:
+		self._columnheaderLastX = first
+		self._columnheader.xview('moveto', first)
+
+    def _scrollYNow(self, first, last):
+        if first == '0' and last == '0':
+            return
+        self._vertScrollbar.set(first, last)
+        self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
+
+        if self['vscrollmode'] == 'dynamic':
+            if self._vertScrollbarNeeded != self._vertScrollbarOn:
+                self._toggleVertScrollbar()
+
+        if self['rowheader']:
+	    if self._rowheaderLastY != first:
+		self._rowheaderLastY = first
+		self._rowheader.yview('moveto', first)
+
+    def _scrollBothLater(self, first, last):
+	# Called by the text widget to set the horizontal or vertical
+	# scrollbar when it has scrolled or changed size or contents.
+
+	if self.scrollTimer is None:
+	    self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+    def _scrollBothNow(self):
+        # This performs the function of _scrollXNow and _scrollYNow.
+        # If one is changed, the other should be updated to match.
+	self.scrollTimer = None
+
+        # Call update_idletasks to make sure that the containing frame
+        # has been resized before we attempt to set the scrollbars. 
+        # Otherwise the scrollbars may be mapped/unmapped continuously.
+        self._scrollRecurse = self._scrollRecurse + 1
+        self.update_idletasks()
+        self._scrollRecurse = self._scrollRecurse - 1
+        if self._scrollRecurse != 0:
+            return
+
+	xview = self._textbox.xview()
+	yview = self._textbox.yview()
+
+	# The text widget returns a yview of (0.0, 0.0) just after it
+	# has been created. Ignore this.
+	if yview == (0.0, 0.0):
+	    return
+
+        if self['columnheader']:
+	    if self._columnheaderLastX != xview[0]:
+		self._columnheaderLastX = xview[0]
+		self._columnheader.xview('moveto', xview[0])
+        if self['rowheader']:
+	    if self._rowheaderLastY != yview[0]:
+		self._rowheaderLastY = yview[0]
+		self._rowheader.yview('moveto', yview[0])
+
+	self._horizScrollbar.set(xview[0], xview[1])
+	self._vertScrollbar.set(yview[0], yview[1])
+
+	self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+	self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+	# If both horizontal and vertical scrollmodes are dynamic and
+	# currently only one scrollbar is mapped and both should be
+	# toggled, then unmap the mapped scrollbar.  This prevents a
+	# continuous mapping and unmapping of the scrollbars. 
+	if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+		self._horizScrollbarNeeded != self._horizScrollbarOn and
+		self._vertScrollbarNeeded != self._vertScrollbarOn and
+		self._vertScrollbarOn != self._horizScrollbarOn):
+	    if self._horizScrollbarOn:
+		self._toggleHorizScrollbar()
+	    else:
+		self._toggleVertScrollbar()
+	    return
+
+	if self['hscrollmode'] == 'dynamic':
+
+	    # The following test is done to prevent continuous
+	    # mapping and unmapping of the horizontal scrollbar. 
+	    # This may occur when some event (scrolling, resizing
+	    # or text changes) modifies the displayed text such
+	    # that the bottom line in the window is the longest
+	    # line displayed.  If this causes the horizontal
+	    # scrollbar to be mapped, the scrollbar may "cover up"
+	    # the bottom line, which would mean that the scrollbar
+	    # is no longer required.  If the scrollbar is then
+	    # unmapped, the bottom line will then become visible
+	    # again, which would cause the scrollbar to be mapped
+	    # again, and so on...
+	    #
+	    # The idea is that, if the width of the text widget
+	    # has not changed and the scrollbar is currently
+	    # mapped, then do not unmap the scrollbar even if it
+	    # is no longer required.  This means that, during
+	    # normal scrolling of the text, once the horizontal
+	    # scrollbar has been mapped it will not be unmapped
+	    # (until the width of the text widget changes).
+
+	    currentWidth = self._textbox.winfo_width()
+	    if self._horizScrollbarNeeded != self._horizScrollbarOn:
+		if self._horizScrollbarNeeded or \
+			self._textWidth != currentWidth:
+		    self._toggleHorizScrollbar()
+	    self._textWidth = currentWidth
+
+	if self['vscrollmode'] == 'dynamic':
+	    if self._vertScrollbarNeeded != self._vertScrollbarOn:
+		self._toggleVertScrollbar()
+
+    def _columnheaderscrolled(self, first, last):
+	if self._textboxLastX != first:
+	    self._textboxLastX = first
+	    self._textbox.xview('moveto', first)
+
+    def _rowheaderscrolled(self, first, last):
+	if self._textboxLastY != first:
+	    self._textboxLastY = first
+	    self._textbox.yview('moveto', first)
+
+    def _toggleHorizScrollbar(self):
+
+	self._horizScrollbarOn = not self._horizScrollbarOn
+
+	interior = self.interior()
+	if self._horizScrollbarOn:
+	    self._horizScrollbar.grid(row = 6, column = 4, sticky = 'news')
+	    interior.grid_rowconfigure(5, minsize = self['scrollmargin'])
+	else:
+	    self._horizScrollbar.grid_forget()
+	    interior.grid_rowconfigure(5, minsize = 0)
+
+    def _toggleVertScrollbar(self):
+
+	self._vertScrollbarOn = not self._vertScrollbarOn
+
+	interior = self.interior()
+	if self._vertScrollbarOn:
+	    self._vertScrollbar.grid(row = 4, column = 6, sticky = 'news')
+	    interior.grid_columnconfigure(5, minsize = self['scrollmargin'])
+	else:
+	    self._vertScrollbar.grid_forget()
+	    interior.grid_columnconfigure(5, minsize = 0)
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+    def bbox(self, index):
+	return self._textbox.bbox(index)
+
+forwardmethods(ScrolledText, Tkinter.Text, '_textbox')
+
+######################################################################
+### File: PmwHistoryText.py
+
+
+_ORIGINAL = 0
+_MODIFIED = 1
+_DISPLAY = 2
+
+class HistoryText(ScrolledText):
+
+    def __init__(self, parent = None, **kw):
+
+        # Define the megawidget options.
+        optiondefs = (
+	    ('compressany',         1,          None),
+	    ('compresstail',        1,          None),
+            ('historycommand',      None,       None),
+        )
+        self.defineoptions(kw, optiondefs)
+
+        # Initialise the base class (after defining the options).
+        ScrolledText.__init__(self, parent)
+
+        # Initialise instance variables.
+	self._list = []
+	self._currIndex = 0
+	self._pastIndex = None
+	self._lastIndex = 0          # pointer to end of history list
+
+        # Check keywords and initialise options.
+        self.initialiseoptions()
+
+    def addhistory(self):
+	text = self.get()
+	if text[-1] == '\n':
+	    text = text[:-1]
+
+	if len(self._list) == 0:
+            # This is the first history entry.  Add it.
+	    self._list.append([text, text, _MODIFIED])
+            return
+
+        currentEntry =  self._list[self._currIndex]
+        if text == currentEntry[_ORIGINAL]:
+            # The current history entry has not been modified. Check if
+            # we need to add it again.
+
+            if self['compresstail'] and self._currIndex == self._lastIndex:
+                return
+
+            if self['compressany']:
+                return
+
+        # Undo any changes for the current history entry, since they
+        # will now be available in the new entry.
+        currentEntry[_MODIFIED] = currentEntry[_ORIGINAL]
+
+        historycommand = self['historycommand']
+        if self._currIndex == self._lastIndex:
+            # The last history entry is currently being displayed,
+            # so disable the special meaning of the 'Next' button.
+            self._pastIndex = None
+            nextState = 'disabled'
+        else:
+            # A previous history entry is currently being displayed,
+            # so allow the 'Next' button to go to the entry after this one.
+            self._pastIndex = self._currIndex
+            nextState = 'normal'
+        if callable(historycommand):
+            historycommand('normal', nextState)
+
+        # Create the new history entry.
+        self._list.append([text, text, _MODIFIED])
+
+        # Move the pointer into the history entry list to the end.
+        self._lastIndex = self._lastIndex + 1
+        self._currIndex = self._lastIndex
+
+    def next(self):
+	if self._currIndex == self._lastIndex and self._pastIndex is None:
+	    self.bell()
+        else:
+            self._modifyDisplay('next')
+
+    def prev(self):
+        self._pastIndex = None
+	if self._currIndex == 0:
+	    self.bell()
+        else:
+            self._modifyDisplay('prev')
+
+    def undo(self):
+	if len(self._list) != 0:
+            self._modifyDisplay('undo')
+
+    def redo(self):
+	if len(self._list) != 0:
+            self._modifyDisplay('redo')
+
+    def gethistory(self):
+        return self._list
+
+    def _modifyDisplay(self, command):
+        # Modify the display to show either the next or previous
+        # history entry (next, prev) or the original or modified
+        # version of the current history entry (undo, redo).
+
+        # Save the currently displayed text.
+        currentText = self.get()
+        if currentText[-1] == '\n':
+            currentText = currentText[:-1]
+
+        currentEntry =  self._list[self._currIndex]
+        if currentEntry[_DISPLAY] == _MODIFIED:
+            currentEntry[_MODIFIED] = currentText
+        elif currentEntry[_ORIGINAL] != currentText:
+            currentEntry[_MODIFIED] = currentText
+            if command in ('next', 'prev'):
+                currentEntry[_DISPLAY] = _MODIFIED
+
+        if command in ('next', 'prev'):
+            prevstate = 'normal'
+            nextstate = 'normal'
+            if command == 'next':
+                if self._pastIndex is not None:
+                    self._currIndex = self._pastIndex
+                    self._pastIndex = None
+                self._currIndex = self._currIndex + 1
+                if self._currIndex == self._lastIndex:
+                    nextstate = 'disabled'
+            elif command == 'prev':
+                self._currIndex = self._currIndex - 1
+                if self._currIndex == 0:
+                    prevstate = 'disabled'
+            historycommand = self['historycommand']
+            if callable(historycommand):
+                historycommand(prevstate, nextstate)
+            currentEntry =  self._list[self._currIndex]
+        else:
+            if command == 'undo':
+                currentEntry[_DISPLAY] = _ORIGINAL
+            elif command == 'redo':
+                currentEntry[_DISPLAY] = _MODIFIED
+
+        # Display the new text.
+        self.delete('1.0', 'end')
+        self.insert('end', currentEntry[currentEntry[_DISPLAY]])
+
+######################################################################
+### File: PmwSelectionDialog.py
+# Not Based on iwidgets version.
+
+
+
+class SelectionDialog(Dialog):
+    # Dialog window with selection list.
+    
+    # Dialog window displaying a list and requesting the user to
+    # select one.
+
+    def __init__(self, parent = None, **kw):
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderx',     10,    INITOPT),
+	    ('bordery',     10,    INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	Dialog.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	aliases = (
+	    ('listbox', 'scrolledlist_listbox'),
+	    ('label', 'scrolledlist_label'),
+	)
+	self._list = self.createcomponent('scrolledlist',
+		aliases, None,
+		ScrolledListBox, (interior,),
+		dblclickcommand = self.invoke)
+	self._list.pack(side='top', expand='true', fill='both',
+		padx = self['borderx'], pady = self['bordery'])
+
+        if not kw.has_key('activatecommand'):
+            # Whenever this dialog is activated, set the focus to the
+            # ScrolledListBox's listbox widget.
+            listbox = self.component('listbox')
+            self.configure(activatecommand = listbox.focus_set)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)size method inherited from Tkinter.Toplevel.Grid.
+    def size(self):
+	return self.component('listbox').size()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Toplevel.Grid.
+    def bbox(self, index):
+	return self.component('listbox').size(index)
+
+forwardmethods(SelectionDialog, ScrolledListBox, '_list')
+
+######################################################################
+### File: PmwTextDialog.py
+# A Dialog with a ScrolledText widget.
+
+
+
+class TextDialog(Dialog):
+    def __init__(self, parent = None, **kw):
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderx',     10,    INITOPT),
+	    ('bordery',     10,    INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	Dialog.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+	aliases = (
+	    ('text', 'scrolledtext_text'),
+	    ('label', 'scrolledtext_label'),
+	)
+	self._text = self.createcomponent('scrolledtext',
+		aliases, None,
+		ScrolledText, (interior,))
+	self._text.pack(side='top', expand=1, fill='both',
+		padx = self['borderx'], pady = self['bordery'])
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Toplevel.Grid.
+    def bbox(self, index):
+	return self._text.bbox(index)
+
+forwardmethods(TextDialog, ScrolledText, '_text')
+
+######################################################################
+### File: PmwTimeCounter.py
+# Authors: Joe VanAndel and Greg McFarlane
+
+import string
+import sys
+import time
+import Tkinter
+
+
+class TimeCounter(MegaWidget):
+    """Up-down counter
+
+    A TimeCounter is a single-line entry widget with Up and Down arrows
+    which increment and decrement the Time value in the entry.  
+    """
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('autorepeat',    1,    None),
+	    ('buttonaspect',  1.0,  INITOPT),
+	    ('command',       None, None),
+	    ('initwait',      300,  None),
+	    ('labelmargin',   0,    INITOPT),
+	    ('labelpos',      None, INITOPT),
+	    ('max',           None, self._max),
+	    ('min',           None, self._min),
+	    ('padx',          0,    INITOPT),
+	    ('pady',          0,    INITOPT),
+	    ('repeatrate',    50,   None),
+	    ('value',         None, INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+    	self.arrowDirection = {}
+	self._flag = 'stopped'
+	self._timerId = None
+
+	self._createComponents(kw)
+
+	value = self['value']
+	if value is None:
+	    now = time.time()
+	    value = time.strftime('%H:%M:%S', time.localtime(now))
+    	self.setvalue(value)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def _createComponents(self, kw):
+
+	# Create the components.
+	interior = self.interior()
+
+	# If there is no label, put the arrows and the entry directly
+	# into the interior, otherwise create a frame for them.  In
+	# either case the border around the arrows and the entry will
+	# be raised (but not around the label).
+	if self['labelpos'] is None:
+	    frame = interior
+            if not kw.has_key('hull_relief'):
+                frame.configure(relief = 'raised')
+            if not kw.has_key('hull_borderwidth'):
+                frame.configure(borderwidth = 1)
+	else:
+	    frame = self.createcomponent('frame',
+		    (), None,
+		    Tkinter.Frame, (interior,),
+                    relief = 'raised', borderwidth = 1)
+	    frame.grid(column=2, row=2, sticky='nsew')
+	    interior.grid_columnconfigure(2, weight=1)
+	    interior.grid_rowconfigure(2, weight=1)
+
+	# Create the down arrow buttons.
+
+	# Create the hour down arrow.
+	self._downHourArrowBtn = self.createcomponent('downhourarrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+    	self.arrowDirection[self._downHourArrowBtn] = 'down'
+	self._downHourArrowBtn.grid(column = 0, row = 2)
+
+	# Create the minute down arrow.
+	self._downMinuteArrowBtn = self.createcomponent('downminutearrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+    	self.arrowDirection[self._downMinuteArrowBtn] = 'down'
+	self._downMinuteArrowBtn.grid(column = 1, row = 2)
+
+	# Create the second down arrow.
+	self._downSecondArrowBtn = self.createcomponent('downsecondarrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+    	self.arrowDirection[self._downSecondArrowBtn] = 'down'
+	self._downSecondArrowBtn.grid(column = 2, row = 2)
+
+	# Create the entry fields.
+
+	# Create the hour entry field.
+	self._hourCounterEntry = self.createcomponent('hourentryfield',
+		(('hourentry', 'hourentryfield_entry'),), None,
+		EntryField, (frame,), validate='integer', entry_width = 2)
+	self._hourCounterEntry.grid(column = 0, row = 1, sticky = 'news')
+
+	# Create the minute entry field.
+	self._minuteCounterEntry = self.createcomponent('minuteentryfield',
+		(('minuteentry', 'minuteentryfield_entry'),), None,
+		EntryField, (frame,), validate='integer', entry_width = 2)
+	self._minuteCounterEntry.grid(column = 1, row = 1, sticky = 'news')
+
+	# Create the second entry field.
+	self._secondCounterEntry = self.createcomponent('secondentryfield',
+		(('secondentry', 'secondentryfield_entry'),), None,
+		EntryField, (frame,), validate='integer', entry_width = 2)
+	self._secondCounterEntry.grid(column = 2, row = 1, sticky = 'news')
+
+	# Create the up arrow buttons.
+
+	# Create the hour up arrow.
+	self._upHourArrowBtn = self.createcomponent('uphourarrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+    	self.arrowDirection[self._upHourArrowBtn] = 'up'
+	self._upHourArrowBtn.grid(column = 0, row = 0)
+
+	# Create the minute up arrow.
+	self._upMinuteArrowBtn = self.createcomponent('upminutearrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+    	self.arrowDirection[self._upMinuteArrowBtn] = 'up'
+	self._upMinuteArrowBtn.grid(column = 1, row = 0)
+
+	# Create the second up arrow.
+	self._upSecondArrowBtn = self.createcomponent('upsecondarrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+    	self.arrowDirection[self._upSecondArrowBtn] = 'up'
+	self._upSecondArrowBtn.grid(column = 2, row = 0)
+
+	# Make it resize nicely.
+	padx = self['padx']
+	pady = self['pady']
+	for col in range(3):
+	    frame.grid_columnconfigure(col, weight = 1, pad = padx)
+	frame.grid_rowconfigure(0, pad = pady)
+	frame.grid_rowconfigure(2, pad = pady)
+
+	frame.grid_rowconfigure(1, weight = 1)
+
+	# Create the label.
+	self.createlabel(interior)
+
+	# Set bindings.
+
+	# Up hour
+	self._upHourArrowBtn.bind('<Configure>', 
+		lambda  event, s=self,button=self._upHourArrowBtn: 
+		s._drawArrow(button, 'up'))
+
+	self._upHourArrowBtn.bind('<1>', 
+    	    	lambda event, s=self,button=self._upHourArrowBtn: 
+		s._countUp(button, 3600))
+
+	self._upHourArrowBtn.bind('<Any-ButtonRelease-1>', 
+		lambda event, s=self, button=self._upHourArrowBtn:
+		s._stopUpDown(button))
+
+	# Up minute
+	self._upMinuteArrowBtn.bind('<Configure>', 
+		lambda  event, s=self,button=self._upMinuteArrowBtn: 
+		s._drawArrow(button, 'up'))
+	    
+
+	self._upMinuteArrowBtn.bind('<1>', 
+    	    	lambda event, s=self,button=self._upMinuteArrowBtn: 
+		s._countUp(button, 60))
+
+	self._upMinuteArrowBtn.bind('<Any-ButtonRelease-1>', 
+		lambda event, s=self, button=self._upMinuteArrowBtn:
+		s._stopUpDown(button))
+
+	# Up second
+	self._upSecondArrowBtn.bind('<Configure>', 
+		lambda  event, s=self,button=self._upSecondArrowBtn: 
+		s._drawArrow(button, 'up'))
+	    
+
+	self._upSecondArrowBtn.bind('<1>', 
+    	    	lambda event, s=self,button=self._upSecondArrowBtn: 
+		s._countUp(button, 1))
+
+	self._upSecondArrowBtn.bind('<Any-ButtonRelease-1>', 
+		lambda event, s=self, button=self._upSecondArrowBtn:
+		s._stopUpDown(button))
+
+	# Down hour
+	self._downHourArrowBtn.bind('<Configure>', 
+		lambda  event, s=self,button=self._downHourArrowBtn: 
+		s._drawArrow(button, 'down'))
+
+	self._downHourArrowBtn.bind('<1>', 
+    	    	lambda event, s=self,button=self._downHourArrowBtn: 
+		s._countDown(button, 3600))
+	self._downHourArrowBtn.bind('<Any-ButtonRelease-1>', 
+		lambda event, s=self, button=self._downHourArrowBtn:
+		s._stopUpDown(button))
+
+
+	# Down minute
+	self._downMinuteArrowBtn.bind('<Configure>', 
+		lambda  event, s=self,button=self._downMinuteArrowBtn: 
+		s._drawArrow(button, 'down'))
+
+	self._downMinuteArrowBtn.bind('<1>', 
+    	    	lambda event, s=self,button=self._downMinuteArrowBtn:
+                s._countDown(button, 60))
+	self._downMinuteArrowBtn.bind('<Any-ButtonRelease-1>', 
+		lambda event, s=self, button=self._downMinuteArrowBtn:
+		s._stopUpDown(button))
+
+	# Down second
+	self._downSecondArrowBtn.bind('<Configure>', 
+		lambda  event, s=self,button=self._downSecondArrowBtn: 
+		s._drawArrow(button, 'down'))
+
+	self._downSecondArrowBtn.bind('<1>', 
+    	    	lambda event, s=self, button=self._downSecondArrowBtn: 
+		s._countDown(button,1))
+	self._downSecondArrowBtn.bind('<Any-ButtonRelease-1>', 
+		lambda event, s=self, button=self._downSecondArrowBtn:
+		s._stopUpDown(button))
+
+	self._hourCounterEntry.component('entry').bind(
+                '<Return>', self._invoke)
+	self._minuteCounterEntry.component('entry').bind(
+        	'<Return>', self._invoke)
+	self._secondCounterEntry.component('entry').bind(
+        	'<Return>', self._invoke)
+
+	self._hourCounterEntry.bind('<Configure>', self._resizeArrow)
+	self._minuteCounterEntry.bind('<Configure>', self._resizeArrow)
+	self._secondCounterEntry.bind('<Configure>', self._resizeArrow)
+
+    def _drawArrow(self, arrow, direction):
+        drawarrow(arrow, self['hourentry_foreground'], direction, 'arrow')
+
+    def _resizeArrow(self, event = None):
+	for btn in (self._upHourArrowBtn, self._upMinuteArrowBtn,
+		self._upSecondArrowBtn,
+		self._downHourArrowBtn,
+		self._downMinuteArrowBtn, self._downSecondArrowBtn):
+	    bw = (string.atoi(btn['borderwidth']) +
+		    string.atoi(btn['highlightthickness']))
+	    newHeight = self._hourCounterEntry.winfo_reqheight() - 2 * bw
+	    newWidth = int(newHeight * self['buttonaspect'])
+	    btn.configure(width=newWidth, height=newHeight)
+	    self._drawArrow(btn, self.arrowDirection[btn])
+
+    def _min(self):
+	min = self['min']
+        if min is None:
+	    self._minVal = 0
+	else:
+	    self._minVal = timestringtoseconds(min)
+
+    def _max(self):
+	max = self['max']
+	if max is None:
+	    self._maxVal = None
+	else:
+	    self._maxVal = timestringtoseconds(max)
+
+    def getvalue(self):
+        return self.getstring()
+
+    def setvalue(self, text):
+        list = string.split(text, ':')
+	if len(list) != 3:
+	    raise ValueError, 'invalid value: ' + text
+
+	self._hour = string.atoi(list[0])
+	self._minute = string.atoi(list[1])
+	self._second = string.atoi(list[2]) 
+
+    	self._setHMS()
+
+    def getstring(self):
+    	return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)
+
+    def getint(self):
+    	return self._hour * 3600 + self._minute * 60 + self._second
+
+    def _countUp(self, button, increment):
+	self._relief = self._upHourArrowBtn.cget('relief')
+	button.configure(relief='sunken')
+	self._count(1, 'start', increment)
+
+    def _countDown(self, button, increment):
+
+	self._relief = self._downHourArrowBtn.cget('relief')
+	button.configure(relief='sunken')
+	self._count(-1, 'start', increment)
+
+    def increment(self, seconds = 1):
+	self._count(1, 'force', seconds)
+
+    def decrement(self, seconds = 1):
+	self._count(-1, 'force', seconds)
+
+    def _count(self, factor, newFlag = None, increment = 1):
+	if newFlag != 'force':
+	  if newFlag is not None:
+	    self._flag = newFlag
+
+	  if self._flag == 'stopped':
+	    return
+
+	value = (string.atoi(self._hourCounterEntry.get()) *3600) + \
+	      (string.atoi(self._minuteCounterEntry.get()) *60) + \
+	      string.atoi(self._secondCounterEntry.get()) + \
+	      factor * increment
+	min = self._minVal
+	max = self._maxVal
+	if value < min:
+	  value = min
+	if max is not None and value > max:
+	  value = max
+
+	self._hour = value /3600
+	self._minute = (value - (self._hour*3600)) / 60
+	self._second = value - (self._hour*3600) - (self._minute*60)
+	self._setHMS()
+
+	if newFlag != 'force':
+	  if self['autorepeat']:
+	    if self._flag == 'start':
+	      delay = self['initwait']
+	      self._flag = 'running'
+	    else:
+	      delay = self['repeatrate']
+	    self._timerId = self.after(
+		delay, lambda self=self, factor=factor,increment=increment: 
+		  self._count(factor,'running', increment))
+
+    def _setHMS(self):
+        self._hourCounterEntry.setentry('%02d' % self._hour)
+        self._minuteCounterEntry.setentry('%02d' % self._minute)
+        self._secondCounterEntry.setentry('%02d' % self._second)
+
+    def _stopUpDown(self, button):
+        if self._timerId is not None:
+            self.after_cancel(self._timerId)
+	    self._timerId = None
+        button.configure(relief=self._relief)
+        self._flag = 'stopped'
+
+    def _invoke(self, event):
+        cmd = self['command']
+        if callable(cmd):
+	    cmd()
+
+    def invoke(self):
+        cmd = self['command']
+        if callable(cmd):
+	    return cmd()
+
+    def destroy(self):
+        if self._timerId is not None:
+            self.after_cancel(self._timerId)
+	    self._timerId = None
+        MegaWidget.destroy(self)
+
+######################################################################
+### File: PmwAboutDialog.py
+
+
+class AboutDialog(MessageDialog):
+    # Window to display version and contact information.
+
+    # Class members containing resettable 'default' values:
+    _version = ''
+    _copyright = ''
+    _contact = ''
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('applicationname',   '',          INITOPT),
+	    ('iconpos',           'w',         None),
+	    ('icon_bitmap',       'info',      None),
+	    ('buttons',           ('Close',),  None),
+	    ('defaultbutton',     0,           None),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MessageDialog.__init__(self, parent)
+
+	applicationname = self['applicationname']
+        if not kw.has_key('title'):
+            self.configure(title = 'About ' + applicationname)
+
+        if not kw.has_key('message_text'):
+            text = applicationname + '\n\n'
+            if AboutDialog._version != '':
+              text = text + 'Version ' + AboutDialog._version + '\n'
+            if AboutDialog._copyright != '':
+              text = text + AboutDialog._copyright + '\n\n'
+            if AboutDialog._contact != '':
+              text = text + AboutDialog._contact
+
+            self.configure(message_text=text)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+def aboutversion(value):
+    AboutDialog._version = value
+
+def aboutcopyright(value):
+    AboutDialog._copyright = value
+
+def aboutcontact(value):
+    AboutDialog._contact = value
+
+######################################################################
+### File: PmwComboBox.py
+# Based on iwidgets2.2.0/combobox.itk code.
+
+import os
+import string
+import types
+import Tkinter
+
+
+class ComboBox(MegaWidget):
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('autoclear',          0,          INITOPT),
+	    ('buttonaspect',       1.0,        INITOPT),
+	    ('dropdown',           1,          INITOPT),
+	    ('fliparrow',          0,          INITOPT),
+	    ('history',            1,          INITOPT),
+	    ('labelmargin',        0,          INITOPT),
+	    ('labelpos',           None,       INITOPT),
+	    ('listheight',         200,        INITOPT),
+	    ('selectioncommand',   None,       None),
+	    ('sticky',            'ew',        INITOPT),
+	    ('unique',             1,          INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	self._entryfield = self.createcomponent('entryfield',
+		(('entry', 'entryfield_entry'),), None,
+		EntryField, (interior,))
+	self._entryfield.grid(column=2, row=2, sticky=self['sticky'])
+	interior.grid_columnconfigure(2, weight = 1)
+	self._entryWidget = self._entryfield.component('entry')
+
+	if self['dropdown']:
+	    self._isPosted = 0
+            interior.grid_rowconfigure(2, weight = 1)
+
+	    # Create the arrow button.
+	    self._arrowBtn = self.createcomponent('arrowbutton',
+		    (), None,
+		    Tkinter.Canvas, (interior,), borderwidth = 2,
+		    relief = 'raised',
+		    width = 16, height = 16)
+            if 'n' in self['sticky']:
+                sticky = 'n'
+            else:
+                sticky = ''
+            if 's' in self['sticky']:
+                sticky = sticky + 's'
+	    self._arrowBtn.grid(column=3, row=2, sticky = sticky)
+	    self._arrowRelief = self._arrowBtn.cget('relief')
+
+	    # Create the label.
+	    self.createlabel(interior, childCols=2)
+
+	    # Create the dropdown window.
+	    self._popup = self.createcomponent('popup',
+		    (), None,
+		    Tkinter.Toplevel, (interior,))
+	    self._popup.withdraw()
+	    self._popup.overrideredirect(1)
+
+	    # Create the scrolled listbox inside the dropdown window.
+	    self._list = self.createcomponent('scrolledlist',
+		    (('listbox', 'scrolledlist_listbox'),), None,
+		    ScrolledListBox, (self._popup,),
+		    hull_borderwidth = 2,
+		    hull_relief = 'raised',
+		    hull_height = self['listheight'],
+		    usehullsize = 1,
+		    listbox_exportselection = 0)
+	    self._list.pack(expand=1, fill='both')
+	    self.__listbox = self._list.component('listbox')
+
+	    # Bind events to the arrow button.
+	    self._arrowBtn.bind('<1>', self._postList)
+	    self._arrowBtn.bind('<Configure>', self._drawArrow)
+	    self._arrowBtn.bind('<3>', self._next)
+	    self._arrowBtn.bind('<Shift-3>', self._previous)
+	    self._arrowBtn.bind('<Down>', self._next)
+	    self._arrowBtn.bind('<Up>', self._previous)
+	    self._arrowBtn.bind('<Control-n>', self._next)
+	    self._arrowBtn.bind('<Control-p>', self._previous)
+	    self._arrowBtn.bind('<Shift-Down>', self._postList)
+	    self._arrowBtn.bind('<Shift-Up>', self._postList)
+	    self._arrowBtn.bind('<F34>', self._postList)
+	    self._arrowBtn.bind('<F28>', self._postList)
+	    self._arrowBtn.bind('<space>', self._postList)
+
+	    # Bind events to the dropdown window.
+	    self._popup.bind('<Escape>', self._unpostList)
+	    self._popup.bind('<space>', self._selectUnpost)
+	    self._popup.bind('<Return>', self._selectUnpost)
+	    self._popup.bind('<ButtonRelease-1>', self._dropdownBtnRelease)
+	    self._popup.bind('<ButtonPress-1>', self._unpostOnNextRelease)
+
+	    # Bind events to the Tk listbox.
+	    self.__listbox.bind('<Enter>', self._unpostOnNextRelease)
+
+	    # Bind events to the Tk entry widget.
+	    self._entryWidget.bind('<Configure>', self._resizeArrow)
+	    self._entryWidget.bind('<Shift-Down>', self._postList)
+	    self._entryWidget.bind('<Shift-Up>', self._postList)
+	    self._entryWidget.bind('<F34>', self._postList)
+	    self._entryWidget.bind('<F28>', self._postList)
+
+            # Need to unpost the popup if the entryfield is unmapped (eg: 
+            # its toplevel window is withdrawn) while the popup list is
+            # displayed.
+            self._entryWidget.bind('<Unmap>', self._unpostList)
+
+	else:
+	    # Create the scrolled listbox below the entry field.
+	    self._list = self.createcomponent('scrolledlist',
+		    (('listbox', 'scrolledlist_listbox'),), None,
+		    ScrolledListBox, (interior,),
+                    selectioncommand = self._selectCmd)
+	    self._list.grid(column=2, row=3, sticky='nsew')
+	    self.__listbox = self._list.component('listbox')
+
+	    # The scrolled listbox should expand vertically.
+	    interior.grid_rowconfigure(3, weight = 1)
+
+	    # Create the label.
+	    self.createlabel(interior, childRows=2)
+
+	self._entryWidget.bind('<Down>', self._next)
+	self._entryWidget.bind('<Up>', self._previous)
+	self._entryWidget.bind('<Control-n>', self._next)
+	self._entryWidget.bind('<Control-p>', self._previous)
+	self.__listbox.bind('<Control-n>', self._next)
+	self.__listbox.bind('<Control-p>', self._previous)
+
+	if self['history']:
+	    self._entryfield.configure(command=self._addHistory)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def destroy(self):
+	if self['dropdown'] and self._isPosted:
+            popgrab(self._popup)
+        MegaWidget.destroy(self)
+
+    #======================================================================
+
+    # Public methods
+
+    def get(self, first = None, last=None):
+	if first is None:
+	    return self._entryWidget.get()
+	else:
+	    return self._list.get(first, last)
+
+    def invoke(self):
+	if self['dropdown']:
+	    self._postList()
+	else:
+	    return self._selectCmd()
+
+    def selectitem(self, index, setentry=1):
+	if type(index) == types.StringType:
+	    text = index
+	    items = self._list.get(0, 'end')
+	    if text in items:
+		index = list(items).index(text)
+	    else:
+	    	raise IndexError, 'index "%s" not found' % text
+	elif setentry:
+	    text = self._list.get(0, 'end')[index]
+
+	self._list.select_clear(0, 'end')
+	self._list.select_set(index, index)
+	self._list.activate(index)
+	self.see(index)
+	if setentry:
+	    self._entryfield.setentry(text)
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)size method inherited from Tkinter.Frame.Grid.
+    def size(self):
+	return self._list.size()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+    def bbox(self, index):
+	return self._list.bbox(index)
+
+    def clear(self):
+	self._entryfield.clear()
+	self._list.clear()
+
+    #======================================================================
+
+    # Private methods for both dropdown and simple comboboxes.
+
+    def _addHistory(self):
+	input = self._entryWidget.get()
+
+	if input != '':
+	    index = None
+	    if self['unique']:
+		# If item is already in list, select it and return.
+		items = self._list.get(0, 'end')
+		if input in items:
+		    index = list(items).index(input)
+
+	    if index is None:
+		index = self._list.index('end')
+		self._list.insert('end', input)
+
+	    self.selectitem(index)
+	    if self['autoclear']:
+		self._entryWidget.delete(0, 'end')
+
+	    # Execute the selectioncommand on the new entry.
+	    self._selectCmd()
+
+    def _next(self, event):
+	size = self.size()
+	if size <= 1:
+	    return
+
+	cursels = self.curselection()
+
+	if len(cursels) == 0:
+	    index = 0
+	else:
+	    index = string.atoi(cursels[0])
+	    if index == size - 1:
+		index = 0
+	    else:
+		index = index + 1
+
+	self.selectitem(index)
+
+    def _previous(self, event):
+	size = self.size()
+	if size <= 1:
+	    return
+
+	cursels = self.curselection()
+
+	if len(cursels) == 0:
+	    index = size - 1
+	else:
+	    index = string.atoi(cursels[0])
+	    if index == 0:
+		index = size - 1
+	    else:
+		index = index - 1
+
+	self.selectitem(index)
+
+    def _selectCmd(self, event=None):
+
+	sels = self.getcurselection()
+	if len(sels) == 0:
+	    item = None
+	else:
+	    item = sels[0]
+	    self._entryfield.setentry(item)
+
+	cmd = self['selectioncommand']
+	if callable(cmd):
+            if event is None:
+                # Return result of selectioncommand for invoke() method.
+                return cmd(item)
+            else:
+                cmd(item)
+
+    #======================================================================
+
+    # Private methods for dropdown combobox.
+
+    def _drawArrow(self, event=None, sunken=0):
+        arrow = self._arrowBtn
+	if sunken:
+	    self._arrowRelief = arrow.cget('relief')
+	    arrow.configure(relief = 'sunken')
+	else:
+	    arrow.configure(relief = self._arrowRelief)
+
+	if self._isPosted and self['fliparrow']:
+            direction = 'up'
+        else:
+            direction = 'down'
+        drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
+
+    def _postList(self, event = None):
+        self._isPosted = 1
+        self._drawArrow(sunken=1)
+
+        # Make sure that the arrow is displayed sunken.
+        self.update_idletasks()
+
+        x = self._entryfield.winfo_rootx()
+        y = self._entryfield.winfo_rooty() + \
+            self._entryfield.winfo_height()
+        w = self._entryfield.winfo_width() + self._arrowBtn.winfo_width()
+        h =  self.__listbox.winfo_height()
+        sh = self.winfo_screenheight()
+
+        if y + h > sh and y > sh / 2:
+            y = self._entryfield.winfo_rooty() - h
+
+        self._list.configure(hull_width=w)
+
+        setgeometryanddeiconify(self._popup, '+%d+%d' % (x, y))
+
+        # Grab the popup, so that all events are delivered to it, and
+        # set focus to the listbox, to make keyboard navigation
+        # easier.
+        pushgrab(self._popup, 1, self._unpostList)
+        self.__listbox.focus_set()
+
+        self._drawArrow()
+
+        # Ignore the first release of the mouse button after posting the
+        # dropdown list, unless the mouse enters the dropdown list.
+        self._ignoreRelease = 1
+
+    def _dropdownBtnRelease(self, event):
+	if (event.widget == self._list.component('vertscrollbar') or
+		event.widget == self._list.component('horizscrollbar')):
+	    return
+
+	if self._ignoreRelease:
+	    self._unpostOnNextRelease()
+	    return
+
+        self._unpostList()
+
+	if (event.x >= 0 and event.x < self.__listbox.winfo_width() and
+		event.y >= 0 and event.y < self.__listbox.winfo_height()):
+	    self._selectCmd()
+
+    def _unpostOnNextRelease(self, event = None):
+	self._ignoreRelease = 0
+
+    def _resizeArrow(self, event):
+	bw = (string.atoi(self._arrowBtn['borderwidth']) + 
+		string.atoi(self._arrowBtn['highlightthickness']))
+	newHeight = self._entryfield.winfo_reqheight() - 2 * bw
+	newWidth = int(newHeight * self['buttonaspect'])
+	self._arrowBtn.configure(width=newWidth, height=newHeight)
+	self._drawArrow()
+
+    def _unpostList(self, event=None):
+	if not self._isPosted:
+            # It is possible to get events on an unposted popup.  For
+            # example, by repeatedly pressing the space key to post
+            # and unpost the popup.  The <space> event may be
+            # delivered to the popup window even though
+            # popgrab() has set the focus away from the
+            # popup window.  (Bug in Tk?)
+            return
+
+        # Restore the focus before withdrawing the window, since
+        # otherwise the window manager may take the focus away so we
+        # can't redirect it.  Also, return the grab to the next active
+        # window in the stack, if any.
+        popgrab(self._popup)
+	self._popup.withdraw()
+
+	self._isPosted = 0
+	self._drawArrow()
+
+    def _selectUnpost(self, event):
+        self._unpostList()
+	self._selectCmd()
+
+forwardmethods(ComboBox, ScrolledListBox, '_list')
+forwardmethods(ComboBox, EntryField, '_entryfield')
+
+######################################################################
+### File: PmwComboBoxDialog.py
+# Not Based on iwidgets version.
+
+
+
+class ComboBoxDialog(Dialog):
+    # Dialog window with simple combobox.
+    
+    # Dialog window displaying a list and entry field and requesting
+    # the user to make a selection or enter a value
+
+    def __init__(self, parent = None, **kw):
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderx',    10,              INITOPT),
+	    ('bordery',    10,              INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	Dialog.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	aliases = (
+	    ('listbox', 'combobox_listbox'),
+	    ('scrolledlist', 'combobox_scrolledlist'),
+	    ('entry', 'combobox_entry'),
+	    ('label', 'combobox_label'),
+	)
+	self._combobox = self.createcomponent('combobox',
+		aliases, None,
+		ComboBox, (interior,),
+		scrolledlist_dblclickcommand = self.invoke,
+		dropdown = 0,
+	)
+	self._combobox.pack(side='top', expand='true', fill='both',
+		padx = self['borderx'], pady = self['bordery'])
+
+        if not kw.has_key('activatecommand'):
+            # Whenever this dialog is activated, set the focus to the
+            # ComboBox's listbox widget.
+            listbox = self.component('listbox')
+            self.configure(activatecommand = listbox.focus_set)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)size method inherited from Tkinter.Toplevel.Grid.
+    def size(self):
+	return self._combobox.size()
+
+    # Need to explicitly forward this to override the stupid
+    # (grid_)bbox method inherited from Tkinter.Toplevel.Grid.
+    def bbox(self, index):
+	return self._combobox.bbox(index)
+
+forwardmethods(ComboBoxDialog, ComboBox, '_combobox')
+
+######################################################################
+### File: PmwCounter.py
+import string
+import sys
+import types
+import Tkinter
+
+
+class Counter(MegaWidget):
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('autorepeat',     1,             None),
+	    ('buttonaspect',   1.0,           INITOPT),
+	    ('datatype',       'numeric',     self._datatype),
+	    ('increment',      1,             None),
+	    ('initwait',       300,           None),
+	    ('labelmargin',    0,             INITOPT),
+	    ('labelpos',       None,          INITOPT),
+	    ('orient',         'horizontal',  INITOPT),
+	    ('padx',           0,             INITOPT),
+	    ('pady',           0,             INITOPT),
+	    ('repeatrate',     50,            None),
+	    ('sticky',         'ew',          INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	MegaWidget.__init__(self, parent)
+
+	# Initialise instance variables.
+	self._timerId = None
+	self._normalRelief = None
+
+	# Create the components.
+	interior = self.interior()
+
+	# If there is no label, put the arrows and the entry directly
+	# into the interior, otherwise create a frame for them.  In
+	# either case the border around the arrows and the entry will
+	# be raised (but not around the label).
+	if self['labelpos'] is None:
+	    frame = interior
+            if not kw.has_key('hull_relief'):
+                frame.configure(relief = 'raised')
+            if not kw.has_key('hull_borderwidth'):
+                frame.configure(borderwidth = 1)
+	else:
+	    frame = self.createcomponent('frame',
+		    (), None,
+		    Tkinter.Frame, (interior,),
+                    relief = 'raised', borderwidth = 1)
+	    frame.grid(column=2, row=2, sticky=self['sticky'])
+	    interior.grid_columnconfigure(2, weight=1)
+	    interior.grid_rowconfigure(2, weight=1)
+
+	# Create the down arrow.
+	self._downArrowBtn = self.createcomponent('downarrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+
+	# Create the entry field.
+	self._counterEntry = self.createcomponent('entryfield',
+		(('entry', 'entryfield_entry'),), None,
+		EntryField, (frame,))
+
+	# Create the up arrow.
+	self._upArrowBtn = self.createcomponent('uparrow',
+		(), 'Arrow',
+		Tkinter.Canvas, (frame,),
+		width = 16, height = 16, relief = 'raised', borderwidth = 2)
+
+	padx = self['padx']
+	pady = self['pady']
+	orient = self['orient']
+	if orient == 'horizontal':
+	    self._downArrowBtn.grid(column = 0, row = 0)
+	    self._counterEntry.grid(column = 1, row = 0,
+                    sticky = self['sticky'])
+	    self._upArrowBtn.grid(column = 2, row = 0)
+	    frame.grid_columnconfigure(1, weight = 1)
+	    frame.grid_rowconfigure(0, weight = 1)
+	    if Tkinter.TkVersion >= 4.2:
+		frame.grid_columnconfigure(0, pad = padx)
+		frame.grid_columnconfigure(2, pad = padx)
+		frame.grid_rowconfigure(0, pad = pady)
+	elif orient == 'vertical':
+	    self._upArrowBtn.grid(column = 0, row = 0, sticky = 's')
+	    self._counterEntry.grid(column = 0, row = 1,
+                    sticky = self['sticky'])
+	    self._downArrowBtn.grid(column = 0, row = 2, sticky = 'n')
+	    frame.grid_columnconfigure(0, weight = 1)
+	    frame.grid_rowconfigure(0, weight = 1)
+	    frame.grid_rowconfigure(2, weight = 1)
+	    if Tkinter.TkVersion >= 4.2:
+		frame.grid_rowconfigure(0, pad = pady)
+		frame.grid_rowconfigure(2, pad = pady)
+		frame.grid_columnconfigure(0, pad = padx)
+	else:
+	    raise ValueError, 'bad orient option ' + repr(orient) + \
+		': must be either \'horizontal\' or \'vertical\''
+
+	self.createlabel(interior)
+
+	self._upArrowBtn.bind('<Configure>', self._drawUpArrow)
+	self._upArrowBtn.bind('<1>', self._countUp)
+	self._upArrowBtn.bind('<Any-ButtonRelease-1>', self._stopCounting)
+	self._downArrowBtn.bind('<Configure>', self._drawDownArrow)
+	self._downArrowBtn.bind('<1>', self._countDown)
+	self._downArrowBtn.bind('<Any-ButtonRelease-1>', self._stopCounting)
+	self._counterEntry.bind('<Configure>', self._resizeArrow)
+	entry = self._counterEntry.component('entry')
+	entry.bind('<Down>', lambda event, s = self: s._key_decrement(event))
+	entry.bind('<Up>', lambda event, s = self: s._key_increment(event))
+
+	# Need to cancel the timer if an arrow button is unmapped (eg: 
+	# its toplevel window is withdrawn) while the mouse button is
+	# held down.  The canvas will not get the ButtonRelease event
+	# if it is not mapped, since the implicit grab is cancelled.
+	self._upArrowBtn.bind('<Unmap>', self._stopCounting)
+	self._downArrowBtn.bind('<Unmap>', self._stopCounting)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    def _resizeArrow(self, event):
+	for btn in (self._upArrowBtn, self._downArrowBtn):
+	    bw = (string.atoi(btn['borderwidth']) +
+		    string.atoi(btn['highlightthickness']))
+	    newHeight = self._counterEntry.winfo_reqheight() - 2 * bw
+	    newWidth = int(newHeight * self['buttonaspect'])
+	    btn.configure(width=newWidth, height=newHeight)
+	    self._drawArrow(btn)
+
+    def _drawUpArrow(self, event):
+	self._drawArrow(self._upArrowBtn)
+
+    def _drawDownArrow(self, event):
+	self._drawArrow(self._downArrowBtn)
+
+    def _drawArrow(self, arrow):
+        if self['orient'] == 'vertical':
+            if arrow == self._upArrowBtn:
+                direction = 'up'
+            else:
+                direction = 'down'
+        else:
+            if arrow == self._upArrowBtn:
+                direction = 'right'
+            else:
+                direction = 'left'
+        drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
+
+    def _stopCounting(self, event = None):
+        if self._timerId is not None:
+            self.after_cancel(self._timerId)
+	    self._timerId = None
+	if self._normalRelief is not None:
+	    button, relief = self._normalRelief
+	    button.configure(relief=relief)
+	    self._normalRelief = None
+
+    def _countUp(self, event):
+	self._normalRelief = (self._upArrowBtn, self._upArrowBtn.cget('relief'))
+	self._upArrowBtn.configure(relief='sunken')
+	# Force arrow down (it may come up immediately, if increment fails).
+	self._upArrowBtn.update_idletasks()
+	self._count(1, 1)
+
+    def _countDown(self, event):
+	self._normalRelief = (self._downArrowBtn, self._downArrowBtn.cget('relief'))
+	self._downArrowBtn.configure(relief='sunken')
+	# Force arrow down (it may come up immediately, if increment fails).
+	self._downArrowBtn.update_idletasks()
+	self._count(-1, 1)
+
+    def increment(self):
+	self._forceCount(1)
+
+    def decrement(self):
+	self._forceCount(-1)
+
+    def _key_increment(self, event):
+	self._forceCount(1)
+	self.update_idletasks()
+
+    def _key_decrement(self, event):
+	self._forceCount(-1)
+	self.update_idletasks()
+
+    def _datatype(self):
+	datatype = self['datatype']
+
+	if type(datatype) is types.DictionaryType:
+	    self._counterArgs = datatype.copy()
+	    if self._counterArgs.has_key('counter'):
+		datatype = self._counterArgs['counter']
+		del self._counterArgs['counter']
+	    else:
+		datatype = 'numeric'
+	else:
+	    self._counterArgs = {}
+
+	if _counterCommands.has_key(datatype):
+	    self._counterCommand = _counterCommands[datatype]
+	elif callable(datatype):
+	    self._counterCommand = datatype
+	else:
+	    validValues = _counterCommands.keys()
+	    validValues.sort()
+	    raise ValueError, ('bad datatype value "%s":  must be a' +
+		    ' function or one of %s') % (datatype, validValues)
+
+    def _forceCount(self, factor):
+	if not self.valid():
+	    self.bell()
+	    return
+
+	text = self._counterEntry.get()
+	try:
+	    value = apply(self._counterCommand,
+		    (text, factor, self['increment']), self._counterArgs)
+	except ValueError:
+	    self.bell()
+	    return
+
+        previousICursor = self._counterEntry.index('insert')
+	if self._counterEntry.setentry(value) == OK:
+	    self._counterEntry.xview('end')
+	    self._counterEntry.icursor(previousICursor)
+
+    def _count(self, factor, first):
+	if not self.valid():
+	    self.bell()
+	    return
+
+	self._timerId = None
+	origtext = self._counterEntry.get()
+	try:
+	    value = apply(self._counterCommand,
+		    (origtext, factor, self['increment']), self._counterArgs)
+	except ValueError:
+	    # If text is invalid, stop counting.
+	    self._stopCounting()
+	    self.bell()
+	    return
+
+	# If incrementing produces an invalid value, restore previous
+	# text and stop counting.
+        previousICursor = self._counterEntry.index('insert')
+	valid = self._counterEntry.setentry(value)
+	if valid != OK:
+	    self._stopCounting()
+	    self._counterEntry.setentry(origtext)
+	    if valid == PARTIAL:
+		self.bell()
+	    return
+	self._counterEntry.xview('end')
+	self._counterEntry.icursor(previousICursor)
+
+	if self['autorepeat']:
+	    if first:
+		delay = self['initwait']
+	    else:
+		delay = self['repeatrate']
+	    self._timerId = self.after(delay,
+		    lambda self=self, factor=factor: self._count(factor, 0))
+
+    def destroy(self):
+	self._stopCounting()
+        MegaWidget.destroy(self)
+
+forwardmethods(Counter, EntryField, '_counterEntry')
+
+def _changeNumber(text, factor, increment):
+  value = string.atol(text)
+  if factor > 0:
+    value = (value / increment) * increment + increment
+  else:
+    value = ((value - 1) / increment) * increment
+
+  # Get rid of the 'L' at the end of longs (in python up to 1.5.2).
+  rtn = str(value)
+  if rtn[-1] == 'L':
+      return rtn[:-1]
+  else:
+      return rtn
+
+def _changeReal(text, factor, increment, separator = '.'):
+  value = stringtoreal(text, separator)
+  div = value / increment
+
+  # Compare reals using str() to avoid problems caused by binary
+  # numbers being only approximations to decimal numbers.
+  # For example, if value is -0.3 and increment is 0.1, then
+  # int(value/increment) = -2, not -3 as one would expect.
+  if str(div)[-2:] == '.0':
+    # value is an even multiple of increment.
+    div = round(div) + factor
+  else:
+    # value is not an even multiple of increment.
+    div = int(div) * 1.0
+    if value < 0:
+      div = div - 1
+    if factor > 0:
+      div = (div + 1)
+
+  value = div * increment
+
+  text = str(value)
+  if separator != '.':
+      index = string.find(text, '.')
+      if index >= 0:
+	text = text[:index] + separator + text[index + 1:]
+  return text
+
+def _changeDate(value, factor, increment, format = 'ymd',
+	separator = '/', yyyy = 0):
+
+  jdn = datestringtojdn(value, format, separator) + factor * increment
+
+  y, m, d = jdntoymd(jdn)
+  result = ''
+  for index in range(3):
+    if index > 0:
+      result = result + separator
+    f = format[index]
+    if f == 'y':
+      if yyyy:
+        result = result + '%02d' % y
+      else:
+        result = result + '%02d' % (y % 100)
+    elif f == 'm':
+      result = result + '%02d' % m
+    elif f == 'd':
+      result = result + '%02d' % d
+
+  return result
+
+_SECSPERDAY = 24 * 60 * 60
+def _changeTime(value, factor, increment, separator = ':', time24 = 0):
+  unixTime = timestringtoseconds(value, separator)
+  if factor > 0:
+    chunks = unixTime / increment + 1
+  else:
+    chunks = (unixTime - 1) / increment
+  unixTime = chunks * increment
+  if time24:
+      while unixTime < 0:
+	  unixTime = unixTime + _SECSPERDAY
+      while unixTime >= _SECSPERDAY:
+	  unixTime = unixTime - _SECSPERDAY
+  if unixTime < 0:
+    unixTime = -unixTime
+    sign = '-'
+  else:
+    sign = ''
+  secs = unixTime % 60
+  unixTime = unixTime / 60
+  mins = unixTime % 60
+  hours = unixTime / 60
+  return '%s%02d%s%02d%s%02d' % (sign, hours, separator, mins, separator, secs)
+
+# hexadecimal, alphabetic, alphanumeric not implemented
+_counterCommands = {
+    'numeric'   : _changeNumber,      # } integer
+    'integer'   : _changeNumber,      # } these two use the same function
+    'real'      : _changeReal,        # real number
+    'time'      : _changeTime,
+    'date'      : _changeDate,
+}
+
+######################################################################
+### File: PmwCounterDialog.py
+
+
+# A Dialog with a counter
+
+class CounterDialog(Dialog):
+
+    def __init__(self, parent = None, **kw):
+
+	# Define the megawidget options.
+	
+	optiondefs = (
+	    ('borderx',    20,  INITOPT),
+	    ('bordery',    20,  INITOPT),
+	)
+	self.defineoptions(kw, optiondefs)
+
+	# Initialise the base class (after defining the options).
+	Dialog.__init__(self, parent)
+
+	# Create the components.
+	interior = self.interior()
+
+	# Create the counter.
+	aliases = (
+	    ('entryfield', 'counter_entryfield'),
+	    ('entry', 'counter_entryfield_entry'),
+	    ('label', 'counter_label')
+	)
+	self._cdCounter = self.createcomponent('counter',
+		aliases, None,
+		Counter, (interior,))
+	self._cdCounter.pack(fill='x', expand=1,
+		padx = self['borderx'], pady = self['bordery'])
+	
+        if not kw.has_key('activatecommand'):
+            # Whenever this dialog is activated, set the focus to the
+            # Counter's entry widget.
+            tkentry = self.component('entry')
+            self.configure(activatecommand = tkentry.focus_set)
+
+	# Check keywords and initialise options.
+	self.initialiseoptions()
+
+    # Supply aliases to some of the entry component methods.
+    def insertentry(self, index, text):
+	self._cdCounter.insert(index, text)
+
+    def deleteentry(self, first, last=None):
+	self._cdCounter.delete(first, last)
+
+    def indexentry(self, index):
+	return self._cdCounter.index(index)
+
+forwardmethods(CounterDialog, Counter, '_cdCounter')
+
+######################################################################
+### File: PmwLogicalFont.py
+import os
+import string
+
+def _font_initialise(root, size=None, fontScheme = None):
+    global _fontSize
+    if size is not None:
+        _fontSize = size
+
+    if fontScheme in ('pmw1', 'pmw2'):
+        if os.name == 'posix':
+            defaultFont = logicalfont('Helvetica')
+            menuFont = logicalfont('Helvetica', weight='bold', slant='italic')
+            scaleFont = logicalfont('Helvetica', slant='italic')
+            root.option_add('*Font',            defaultFont,  'userDefault')
+            root.option_add('*Menu*Font',       menuFont,     'userDefault')
+            root.option_add('*Menubutton*Font', menuFont,     'userDefault')
+            root.option_add('*Scale.*Font',     scaleFont,    'userDefault')
+
+            if fontScheme == 'pmw1':
+                balloonFont = logicalfont('Helvetica', -6, pixel = '12')
+            else: # fontScheme == 'pmw2'
+                balloonFont = logicalfont('Helvetica', -2)
+            root.option_add('*Balloon.*Font', balloonFont, 'userDefault')
+        else:
+            defaultFont = logicalfont('Helvetica')
+            root.option_add('*Font', defaultFont,  'userDefault')
+    elif fontScheme == 'default':
+        defaultFont = ('Helvetica', '-%d' % (_fontSize,), 'bold')
+        entryFont = ('Helvetica', '-%d' % (_fontSize,))
+        textFont = ('Courier', '-%d' % (_fontSize,))
+        root.option_add('*Font',            defaultFont,  'userDefault')
+        root.option_add('*Entry*Font',      entryFont,    'userDefault')
+        root.option_add('*Text*Font',       textFont,     'userDefault')
+
+def logicalfont(name='Helvetica', sizeIncr = 0, **kw):
+  if not _fontInfo.has_key(name):
+    raise ValueError, 'font %s does not exist' % name
+
+  rtn = []
+  for field in _fontFields:
+    if kw.has_key(field):
+      logicalValue = kw[field]
+    elif _fontInfo[name].has_key(field):
+      logicalValue = _fontInfo[name][field]
+    else:
+      logicalValue = '*'
+
+    if _propertyAliases[name].has_key((field, logicalValue)):
+      realValue = _propertyAliases[name][(field, logicalValue)]
+    elif _propertyAliases[name].has_key((field, None)):
+      realValue = _propertyAliases[name][(field, None)]
+    elif _propertyAliases[None].has_key((field, logicalValue)):
+      realValue = _propertyAliases[None][(field, logicalValue)]
+    elif _propertyAliases[None].has_key((field, None)):
+      realValue = _propertyAliases[None][(field, None)]
+    else:
+      realValue = logicalValue
+
+    if field == 'size':
+      if realValue == '*':
+	  realValue = _fontSize
+      realValue = str((realValue + sizeIncr) * 10)
+
+    rtn.append(realValue)
+
+  return string.join(rtn, '-')
+
+def logicalfontnames():
+  return _fontInfo.keys()
+
+if os.name == 'nt':
+    _fontSize = 16
+else:
+    _fontSize = 14
+
+_fontFields = (
+  'registry', 'foundry', 'family', 'weight', 'slant', 'width', 'style',
+  'pixel', 'size', 'xres', 'yres', 'spacing', 'avgwidth', 'charset', 'encoding')
+
+# <_propertyAliases> defines other names for which property values may
+# be known by.  This is required because italics in adobe-helvetica
+# are specified by 'o', while other fonts use 'i'.
+
+_propertyAliases = {}
+
+_propertyAliases[None] = {
+  ('slant', 'italic') : 'i',
+  ('slant', 'normal') : 'r',
+  ('weight', 'light') : 'normal',
+  ('width', 'wide') : 'normal',
+  ('width', 'condensed') : 'normal',
+}
+
+# <_fontInfo> describes a 'logical' font, giving the default values of
+# some of its properties.
+
+_fontInfo = {}
+
+_fontInfo['Helvetica'] = {
+  'foundry' : 'adobe',
+  'family' : 'helvetica',
+  'registry' : '',
+  'charset' : 'iso8859',
+  'encoding' : '1',
+  'spacing' : 'p',
+  'slant' : 'normal',
+  'width' : 'normal',
+  'weight' : 'normal',
+}
+
+_propertyAliases['Helvetica'] = {
+  ('slant', 'italic') : 'o',
+  ('weight', 'normal') : 'medium',
+  ('weight', 'light') : 'medium',
+}
+
+_fontInfo['Times'] = {
+  'foundry' : 'adobe',
+  'family' : 'times',
+  'registry' : '',
+  'charset' : 'iso8859',
+  'encoding' : '1',
+  'spacing' : 'p',
+  'slant' : 'normal',
+  'width' : 'normal',
+  'weight' : 'normal',
+}
+
+_propertyAliases['Times'] = {
+  ('weight', 'normal') : 'medium',
+  ('weight', 'light') : 'medium',
+}
+
+_fontInfo['Fixed'] = {
+  'foundry' : 'misc',
+  'family' : 'fixed',
+  'registry' : '',
+  'charset' : 'iso8859',
+  'encoding' : '1',
+  'spacing' : 'c',
+  'slant' : 'normal',
+  'width' : 'normal',
+  'weight' : 'normal',
+}
+
+_propertyAliases['Fixed'] = {
+  ('weight', 'normal') : 'medium',
+  ('weight', 'light') : 'medium',
+  ('style', None) : '',
+  ('width', 'condensed') : 'semicondensed',
+}
+
+_fontInfo['Courier'] = {
+  'foundry' : 'adobe',
+  'family' : 'courier',
+  'registry' : '',
+  'charset' : 'iso8859',
+  'encoding' : '1',
+  'spacing' : 'm',
+  'slant' : 'normal',
+  'width' : 'normal',
+  'weight' : 'normal',
+}
+
+_propertyAliases['Courier'] = {
+  ('weight', 'normal') : 'medium',
+  ('weight', 'light') : 'medium',
+  ('style', None) : '',
+}
+
+_fontInfo['Typewriter'] = {
+  'foundry' : 'b&h',
+  'family' : 'lucidatypewriter',
+  'registry' : '',
+  'charset' : 'iso8859',
+  'encoding' : '1',
+  'spacing' : 'm',
+  'slant' : 'normal',
+  'width' : 'normal',
+  'weight' : 'normal',
+}
+
+_propertyAliases['Typewriter'] = {
+  ('weight', 'normal') : 'medium',
+  ('weight', 'light') : 'medium',
+}
+
+if os.name == 'nt':
+    # For some reason 'fixed' fonts on NT aren't.
+    _fontInfo['Fixed'] = _fontInfo['Courier']
+    _propertyAliases['Fixed'] = _propertyAliases['Courier']
Index: /inundation/pmesh/PmwBlt.py
===================================================================
--- /inundation/pmesh/PmwBlt.py	(revision 1719)
+++ /inundation/pmesh/PmwBlt.py	(revision 1719)
@@ -0,0 +1,643 @@
+# Python interface to some of the commands of the 2.4 version of the
+# BLT extension to tcl.
+
+import string
+import types
+import Tkinter
+
+# Supported commands:
+_busyCommand = '::blt::busy'
+_vectorCommand = '::blt::vector'
+_graphCommand = '::blt::graph'
+_testCommand = '::blt::*'
+_chartCommand = '::blt::stripchart'
+_tabsetCommand = '::blt::tabset'
+
+_haveBlt = None
+_haveBltBusy = None
+
+def _checkForBlt(window):
+    global _haveBlt
+    global _haveBltBusy
+
+    # Blt may be a package which has not yet been loaded. Try to load it.
+    try:
+	window.tk.call('package', 'require', 'BLT')
+    except Tkinter.TclError:
+	# Another way to try to dynamically load blt:
+	try:
+	    window.tk.call('load', '', 'Blt')
+	except Tkinter.TclError:
+	    pass
+
+    _haveBlt= (window.tk.call('info', 'commands', _testCommand) != '')
+    _haveBltBusy = (window.tk.call('info', 'commands', _busyCommand) != '')
+
+def haveblt(window):
+    if _haveBlt is None:
+	_checkForBlt(window)
+    return _haveBlt
+
+def havebltbusy(window):
+    if _haveBlt is None:
+	_checkForBlt(window)
+    return _haveBltBusy
+
+def _loadBlt(window):
+    if _haveBlt is None:
+	if window is None:
+	    window = Tkinter._default_root
+	    if window is None:
+	    	window = Tkinter.Tk()
+	_checkForBlt(window)
+
+def busy_hold(window, cursor = None):
+    _loadBlt(window)
+    if cursor is None:
+        window.tk.call(_busyCommand, 'hold', window._w)
+    else:
+        window.tk.call(_busyCommand, 'hold', window._w, '-cursor', cursor)
+
+def busy_release(window):
+    _loadBlt(window)
+    window.tk.call(_busyCommand, 'release', window._w)
+
+def busy_forget(window):
+    _loadBlt(window)
+    window.tk.call(_busyCommand, 'forget', window._w)
+
+#=============================================================================
+# Interface to the blt vector command which makes it look like the
+# builtin python list type.
+# The -variable, -command, -watchunset creation options are not supported.
+# The dup, merge, notify, offset, populate, seq and variable methods
+# and the +, -, * and / operations are not supported.
+
+# Blt vector functions:
+def vector_expr(expression):
+    tk = Tkinter._default_root.tk
+    strList = tk.splitlist(tk.call(_vectorCommand, 'expr', expression))
+    return tuple(map(string.atof, strList))
+
+def vector_names(pattern = None):
+    tk = Tkinter._default_root.tk
+    return tk.splitlist(tk.call(_vectorCommand, 'names', pattern))
+
+class Vector:
+    _varnum = 0
+    def __init__(self, size=None, master=None):
+        # <size> can be either an integer size, or a string "first:last".
+	_loadBlt(master)
+	if master:
+	    self._master = master
+	else:
+	    self._master = Tkinter._default_root
+	self.tk = self._master.tk
+	self._name = 'PY_VEC' + str(Vector._varnum)
+	Vector._varnum = Vector._varnum + 1
+	if size is None:
+	    self.tk.call(_vectorCommand, 'create', self._name)
+	else:
+	  self.tk.call(_vectorCommand, 'create', '%s(%s)' % (self._name, size))
+    def __del__(self):
+	self.tk.call(_vectorCommand, 'destroy', self._name)
+    def __str__(self):
+	return self._name
+
+    def __repr__(self):
+	return '[' + string.join(map(str, self), ', ') + ']'
+    def __cmp__(self, list):
+	return cmp(self[:], list)
+
+    def __len__(self): 
+	return self.tk.getint(self.tk.call(self._name, 'length'))
+    def __getitem__(self, key): 
+	oldkey = key
+	if key < 0:
+	    key = key + len(self)
+	try:
+	    return self.tk.getdouble(self.tk.globalgetvar(self._name, str(key)))
+        except Tkinter.TclError:
+	    raise IndexError, oldkey
+    def __setitem__(self, key, value): 
+	if key < 0:
+	    key = key + len(self)
+	return self.tk.globalsetvar(self._name, str(key), float(value))
+
+    def __delitem__(self, key):
+	if key < 0:
+	    key = key + len(self)
+	return self.tk.globalunsetvar(self._name, str(key))
+
+    def __getslice__(self, start, end):
+	length = len(self)
+	if start < 0:
+	    start = 0
+	if end > length:
+	    end = length
+	if start >= end:
+	    return []
+	end = end - 1  # Blt vector slices include end point.
+	text = self.tk.globalgetvar(self._name, str(start) + ':' + str(end))
+	return map(self.tk.getdouble, self.tk.splitlist(text))
+
+    def __setslice__(self, start, end, list):
+	if start > end:
+	    end = start
+	self.set(self[:start] + list + self[end:])
+
+    def __delslice__(self, start, end):
+	if start < end:
+	    self.set(self[:start] + self[end:])
+
+    def __add__(self, list):
+	return self[:] + list
+    def __radd__(self, list):
+	return list + self[:]
+    def __mul__(self, n):
+	return self[:] * n
+    __rmul__ = __mul__
+
+    # Python builtin list methods:
+    def append(self, *args):
+	self.tk.call(self._name, 'append', args)
+    def count(self, obj):
+	return self[:].count(obj)
+    def index(self, value):
+	return self[:].index(value)
+    def insert(self, index, value):
+	self[index:index] = [value]
+    def remove(self, value):
+	del self[self.index(value)]
+    def reverse(self):
+	s = self[:]
+	s.reverse()
+	self.set(s)
+    def sort(self, *args):
+	s = self[:]
+	s.sort()
+	self.set(s)
+
+    # Blt vector instance methods:
+    # append - same as list method above
+    def clear(self):
+	self.tk.call(self._name, 'clear')
+    def delete(self, *args):
+	self.tk.call((self._name, 'delete') + args)
+    def expr(self, expression):
+	self.tk.call(self._name, 'expr', expression)
+    def length(self, newSize=None): 
+	return self.tk.getint(self.tk.call(self._name, 'length', newSize))
+    def range(self, first, last=None):
+	# Note that, unlike self[first:last], this includes the last
+	# item in the returned range.
+	text = self.tk.call(self._name, 'range', first, last)
+	return map(self.tk.getdouble, self.tk.splitlist(text))
+    def search(self, start, end=None):
+	return self._master._getints(self.tk.call(
+		self._name, 'search', start, end))
+    def set(self, list):
+	if type(list) != types.TupleType:
+	    list = tuple(list)
+	self.tk.call(self._name, 'set', list)
+
+    # The blt vector sort method has different semantics to the python
+    # list sort method.  Call these blt_sort:
+    def blt_sort(self, *args):
+	self.tk.call((self._name, 'sort') + args)
+    def blt_sort_reverse(self, *args):
+	self.tk.call((self._name, 'sort', '-reverse') + args)
+
+    # Special blt vector indexes:
+    def min(self):
+	return self.tk.getdouble(self.tk.globalgetvar(self._name, 'min'))
+    def max(self):
+	return self.tk.getdouble(self.tk.globalgetvar(self._name, 'max'))
+
+    # Method borrowed from Tkinter.Var class:
+    def get(self):
+	return self[:]
+
+#=============================================================================
+
+# This is a general purpose configure routine which can handle the
+# configuration of widgets, items within widgets, etc.  Supports the
+# forms configure() and configure('font') for querying and
+# configure(font = 'fixed', text = 'hello') for setting.
+
+def _doConfigure(widget, subcommand, option, kw):
+
+    if not option and not kw:
+        # Return a description of all options.
+        ret = {}
+        options = widget.tk.splitlist(widget.tk.call(subcommand))
+        for optionString in options:
+            optionInfo = widget.tk.splitlist(optionString)
+            option = optionInfo[0][1:]
+            ret[option] = (option,) + optionInfo[1:]
+        return ret
+
+    if option:
+        # Return a description of the option given by <option>.
+        if kw:
+            # Having keywords implies setting configuration options.
+            # Can't set and get in one command!
+            raise ValueError, 'cannot have option argument with keywords'
+        option = '-' + option
+        optionInfo = widget.tk.splitlist(widget.tk.call(subcommand + (option,)))
+        return (optionInfo[0][1:],) + optionInfo[1:]
+
+    # Otherwise, set the given configuration options.
+    widget.tk.call(subcommand + widget._options(kw))
+
+#=============================================================================
+
+class Graph(Tkinter.Widget):
+    # Wrapper for the blt graph widget, version 2.4.
+
+    def __init__(self, master=None, cnf={}, **kw):
+	_loadBlt(master)
+	Tkinter.Widget.__init__(self, master, _graphCommand, cnf, kw)
+
+    def bar_create(self, name, **kw):
+	self.tk.call((self._w, 'bar', 'create', name) + self._options(kw))
+
+    def line_create(self, name, **kw):
+	self.tk.call((self._w, 'line', 'create', name) + self._options(kw))
+
+    def extents(self, item):
+	return self.tk.getint(self.tk.call(self._w, 'extents', item))
+
+    def invtransform(self, winX, winY):
+	return self._getdoubles(
+		self.tk.call(self._w, 'invtransform', winX, winY))
+
+    def inside(self, x, y):
+	return self.tk.getint(self.tk.call(self._w, 'inside', x, y))
+
+    def snap(self, photoName):
+	self.tk.call(self._w, 'snap', photoName)
+
+    def transform(self, x, y):
+	return self._getdoubles(self.tk.call(self._w, 'transform', x, y))
+
+    def axis_cget(self, axisName, key):
+	return self.tk.call(self._w, 'axis', 'cget', axisName, '-' + key)
+    def axis_configure(self, axes, option=None, **kw):
+        # <axes> may be a list of axisNames.
+	if type(axes) == types.StringType:
+            axes = [axes]
+	subcommand = (self._w, 'axis', 'configure') + tuple(axes)
+	return _doConfigure(self, subcommand, option, kw)
+    def axis_create(self, axisName, **kw):
+	self.tk.call((self._w, 'axis', 'create', axisName) + self._options(kw))
+    def axis_delete(self, *args):
+	self.tk.call((self._w, 'axis', 'delete') + args)
+    def axis_invtransform(self, axisName, value):
+	return self.tk.getdouble(self.tk.call(
+		self._w, 'axis', 'invtransform', axisName, value))
+    def axis_limits(self, axisName):
+	return self._getdoubles(self.tk.call(
+		self._w, 'axis', 'limits', axisName))
+    def axis_names(self, *args):
+        return self.tk.splitlist(
+                self.tk.call((self._w, 'axis', 'names') + args))
+    def axis_transform(self, axisName, value):
+	return self.tk.getint(self.tk.call(
+		self._w, 'axis', 'transform', axisName, value))
+
+    def xaxis_cget(self, key):
+	return self.tk.call(self._w, 'xaxis', 'cget', '-' + key)
+    def xaxis_configure(self, option=None, **kw):
+	subcommand = (self._w, 'xaxis', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def xaxis_invtransform(self, value):
+	return self.tk.getdouble(self.tk.call(
+		self._w, 'xaxis', 'invtransform', value))
+    def xaxis_limits(self):
+	return self._getdoubles(self.tk.call(self._w, 'xaxis', 'limits'))
+    def xaxis_transform(self, value):
+	return self.tk.getint(self.tk.call(
+		self._w, 'xaxis', 'transform', value))
+    def xaxis_use(self, axisName = None):
+	return self.tk.call(self._w, 'xaxis', 'use', axisName)
+
+    def x2axis_cget(self, key):
+	return self.tk.call(self._w, 'x2axis', 'cget', '-' + key)
+    def x2axis_configure(self, option=None, **kw):
+	subcommand = (self._w, 'x2axis', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def x2axis_invtransform(self, value):
+	return self.tk.getdouble(self.tk.call(
+		self._w, 'x2axis', 'invtransform', value))
+    def x2axis_limits(self):
+	return self._getdoubles(self.tk.call(self._w, 'x2axis', 'limits'))
+    def x2axis_transform(self, value):
+	return self.tk.getint(self.tk.call(
+		self._w, 'x2axis', 'transform', value))
+    def x2axis_use(self, axisName = None):
+	return self.tk.call(self._w, 'x2axis', 'use', axisName)
+
+    def yaxis_cget(self, key):
+	return self.tk.call(self._w, 'yaxis', 'cget', '-' + key)
+    def yaxis_configure(self, option=None, **kw):
+	subcommand = (self._w, 'yaxis', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def yaxis_invtransform(self, value):
+	return self.tk.getdouble(self.tk.call(
+		self._w, 'yaxis', 'invtransform', value))
+    def yaxis_limits(self):
+	return self._getdoubles(self.tk.call(self._w, 'yaxis', 'limits'))
+    def yaxis_transform(self, value):
+	return self.tk.getint(self.tk.call(
+		self._w, 'yaxis', 'transform', value))
+    def yaxis_use(self, axisName = None):
+	return self.tk.call(self._w, 'yaxis', 'use', axisName)
+
+    def y2axis_cget(self, key):
+	return self.tk.call(self._w, 'y2axis', 'cget', '-' + key)
+    def y2axis_configure(self, option=None, **kw):
+	subcommand = (self._w, 'y2axis', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def y2axis_invtransform(self, value):
+	return self.tk.getdouble(self.tk.call(
+		self._w, 'y2axis', 'invtransform', value))
+    def y2axis_limits(self):
+	return self._getdoubles(self.tk.call(self._w, 'y2axis', 'limits'))
+    def y2axis_transform(self, value):
+	return self.tk.getint(self.tk.call(
+		self._w, 'y2axis', 'transform', value))
+    def y2axis_use(self, axisName = None):
+	return self.tk.call(self._w, 'y2axis', 'use', axisName)
+
+    def crosshairs_cget(self, key):
+	return self.tk.call(self._w, 'crosshairs', 'cget', '-' + key)
+    def crosshairs_configure(self, option=None, **kw):
+	subcommand = (self._w, 'crosshairs', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def crosshairs_off(self):
+	self.tk.call(self._w, 'crosshairs', 'off')
+    def crosshairs_on(self):
+	self.tk.call(self._w, 'crosshairs', 'on')
+    def crosshairs_toggle(self):
+	self.tk.call(self._w, 'crosshairs', 'toggle')
+
+    def element_activate(self, name, *args):
+	self.tk.call((self._w, 'element', 'activate', name) + args)
+    def element_bind(self, tagName, sequence=None, func=None, add=None):
+        return self._bind((self._w, 'element', 'bind', tagName),
+                sequence, func, add)
+    def element_unbind(self, tagName, sequence, funcid=None):
+        self.tk.call(self._w, 'element', 'bind', tagName, sequence, '')
+        if funcid:
+            self.deletecommand(funcid)
+
+    def element_cget(self, name, key):
+	return self.tk.call(self._w, 'element', 'cget', name, '-' + key)
+
+    def element_closest(self, x, y, *args, **kw):
+        var = 'python_private_1'
+	success = self.tk.getint(self.tk.call(
+                (self._w, 'element', 'closest', x, y, var) +
+                        self._options(kw) + args))
+	if success:
+	    rtn = {}
+	    rtn['dist'] = self.tk.getdouble(self.tk.globalgetvar(var, 'dist'))
+	    rtn['x'] = self.tk.getdouble(self.tk.globalgetvar(var, 'x'))
+	    rtn['y'] = self.tk.getdouble(self.tk.globalgetvar(var, 'y'))
+	    rtn['index'] = self.tk.getint(self.tk.globalgetvar(var, 'index'))
+	    rtn['name'] = self.tk.globalgetvar(var, 'name')
+	    return rtn
+	else:
+	    return None
+
+    def element_configure(self, names, option=None, **kw):
+        # <names> may be a list of elemNames.
+	if type(names) == types.StringType:
+            names = [names]
+	subcommand = (self._w, 'element', 'configure') + tuple(names)
+	return _doConfigure(self, subcommand, option, kw)
+
+    def element_deactivate(self, *args):
+	self.tk.call((self._w, 'element', 'deactivate') + args)
+
+    def element_delete(self, *args):
+	self.tk.call((self._w, 'element', 'delete') + args)
+    def element_exists(self, name):
+	return self.tk.getboolean(
+		self.tk.call(self._w, 'element', 'exists', name))
+
+    def element_names(self, *args):
+        return self.tk.splitlist(
+                self.tk.call((self._w, 'element', 'names') + args))
+    def element_show(self, nameList=None):
+	if nameList is not None:
+	    nameList = tuple(nameList)
+	return self.tk.splitlist(
+		self.tk.call(self._w, 'element', 'show', nameList))
+    def element_type(self, name):
+	return self.tk.call(self._w, 'element', 'type', name)
+
+    def grid_cget(self, key):
+	return self.tk.call(self._w, 'grid', 'cget', '-' + key)
+    def grid_configure(self, option=None, **kw):
+	subcommand = (self._w, 'grid', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+
+    def grid_off(self):
+	self.tk.call(self._w, 'grid', 'off')
+    def grid_on(self):
+	self.tk.call(self._w, 'grid', 'on')
+    def grid_toggle(self):
+	self.tk.call(self._w, 'grid', 'toggle')
+
+    def legend_activate(self, *args):
+	self.tk.call((self._w, 'legend', 'activate') + args)
+    def legend_bind(self, tagName, sequence=None, func=None, add=None):
+        return self._bind((self._w, 'legend', 'bind', tagName),
+                sequence, func, add)
+    def legend_unbind(self, tagName, sequence, funcid=None):
+        self.tk.call(self._w, 'legend', 'bind', tagName, sequence, '')
+        if funcid:
+            self.deletecommand(funcid)
+
+    def legend_cget(self, key):
+	return self.tk.call(self._w, 'legend', 'cget', '-' + key)
+    def legend_configure(self, option=None, **kw):
+	subcommand = (self._w, 'legend', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def legend_deactivate(self, *args):
+	self.tk.call((self._w, 'legend', 'deactivate') + args)
+    def legend_get(self, pos):
+	return self.tk.call(self._w, 'legend', 'get', pos)
+
+    def pen_cget(self, name, key):
+	return self.tk.call(self._w, 'pen', 'cget', name, '-' + key)
+    def pen_configure(self, names, option=None, **kw):
+        # <names> may be a list of penNames.
+	if type(names) == types.StringType:
+            names = [names]
+	subcommand = (self._w, 'pen', 'configure') + tuple(names)
+	return _doConfigure(self, subcommand, option, kw)
+    def pen_create(self, name, **kw):
+	self.tk.call((self._w, 'pen', 'create', name) + self._options(kw))
+    def pen_delete(self, *args):
+	self.tk.call((self._w, 'pen', 'delete') + args)
+    def pen_names(self, *args):
+        return self.tk.splitlist(self.tk.call((self._w, 'pen', 'names') + args))
+
+    def postscript_cget(self, key):
+	return self.tk.call(self._w, 'postscript', 'cget', '-' + key)
+    def postscript_configure(self, option=None, **kw):
+	subcommand = (self._w, 'postscript', 'configure')
+	return _doConfigure(self, subcommand, option, kw)
+    def postscript_output(self, fileName=None, **kw):
+	prefix = (self._w, 'postscript', 'output')
+	if fileName is None:
+	    return self.tk.call(prefix + self._options(kw))
+	else:
+	    self.tk.call(prefix + (fileName,) + self._options(kw))
+
+    def marker_after(self, first, second=None):
+	self.tk.call(self._w, 'marker', 'after', first, second)
+    def marker_before(self, first, second=None):
+	self.tk.call(self._w, 'marker', 'before', first, second)
+    def marker_bind(self, tagName, sequence=None, func=None, add=None):
+        return self._bind((self._w, 'marker', 'bind', tagName),
+                sequence, func, add)
+    def marker_unbind(self, tagName, sequence, funcid=None):
+        self.tk.call(self._w, 'marker', 'bind', tagName, sequence, '')
+        if funcid:
+            self.deletecommand(funcid)
+
+    def marker_cget(self, name, key):
+	return self.tk.call(self._w, 'marker', 'cget', name, '-' + key)
+    def marker_configure(self, names, option=None, **kw):
+        # <names> may be a list of markerIds.
+	if type(names) == types.StringType:
+            names = [names]
+	subcommand = (self._w, 'marker', 'configure') + tuple(names)
+	return _doConfigure(self, subcommand, option, kw)
+    def marker_create(self, type, **kw):
+	return self.tk.call(
+                (self._w, 'marker', 'create', type) + self._options(kw))
+
+    def marker_delete(self, *args):
+	self.tk.call((self._w, 'marker', 'delete') + args)
+    def marker_exists(self, name):
+	return self.tk.getboolean(
+		self.tk.call(self._w, 'marker', 'exists', name))
+    def marker_names(self, *args):
+        return self.tk.splitlist(
+                self.tk.call((self._w, 'marker', 'names') + args))
+    def marker_type(self, name):
+	type = self.tk.call(self._w, 'marker', 'type', name)
+	if type == '':
+	    type = None
+	return type
+
+#=============================================================================
+class Stripchart(Graph):
+    # Wrapper for the blt stripchart widget, version 2.4.
+
+    def __init__(self, master=None, cnf={}, **kw):
+	_loadBlt(master)
+	Tkinter.Widget.__init__(self, master, _chartCommand, cnf, kw)
+
+#=============================================================================
+class Tabset(Tkinter.Widget): 
+
+    # Wrapper for the blt TabSet widget, version 2.4.
+
+    def __init__(self, master=None, cnf={}, **kw):
+	_loadBlt(master)
+	Tkinter.Widget.__init__(self, master, _tabsetCommand, cnf, kw)
+
+    def activate(self, tabIndex):
+        self.tk.call(self._w, 'activate', tabIndex)
+
+    # This is the 'bind' sub-command:
+    def tag_bind(self, tagName, sequence=None, func=None, add=None):
+        return self._bind((self._w, 'bind', tagName), sequence, func, add)
+
+    def tag_unbind(self, tagName, sequence, funcid=None):
+        self.tk.call(self._w, 'bind', tagName, sequence, '')
+        if funcid:
+            self.deletecommand(funcid)
+
+    def delete(self, first, last = None):
+    	self.tk.call(self._w, 'delete', first, last)
+
+    # This is the 'focus' sub-command:
+    def tab_focus(self, tabIndex):
+    	self.tk.call(self._w, 'focus', tabIndex)
+    	
+    def get(self, tabIndex):
+	return self.tk.call(self._w, 'get', tabIndex)
+
+    def index(self, tabIndex):
+	index = self.tk.call(self._w, 'index', tabIndex)
+        if index == '':
+            return None
+        else:
+            return self.tk.getint(self.tk.call(self._w, 'index', tabIndex))
+
+    def insert(self, position, name1, *names, **kw):
+	self.tk.call(
+            (self._w, 'insert', position, name1) + names + self._options(kw))
+
+    def invoke(self, tabIndex):
+    	return self.tk.call(self._w, 'invoke', tabIndex)
+
+    def move(self, tabIndex1, beforeOrAfter, tabIndex2):
+    	self.tk.call(self._w, 'move', tabIndex1, beforeOrAfter, tabIndex2)
+    	
+    def nearest(self, x, y):
+    	return self.tk.call(self._w, 'nearest', x, y)
+
+    def scan_mark(self, x, y):
+        self.tk.call(self._w, 'scan', 'mark', x, y)
+
+    def scan_dragto(self, x, y):
+        self.tk.call(self._w, 'scan', 'dragto', x, y)
+
+    def see(self, index):
+        self.tk.call(self._w, 'see', index)
+    	
+    def see(self, tabIndex):
+    	self.tk.call(self._w,'see',tabIndex)
+    	
+    def size(self):
+	return self.tk.getint(self.tk.call(self._w, 'size'))
+
+    def tab_cget(self, tabIndex, option):
+        if option[:1] != '-':
+            option = '-' + option
+        if option[-1:] == '_':
+            option = option[:-1]
+        return self.tk.call(self._w, 'tab', 'cget', tabIndex, option)
+
+    def tab_configure(self, tabIndexes, option=None, **kw):
+        # <tabIndexes> may be a list of tabs.
+	if type(tabIndexes) in (types.StringType, types.IntType):
+            tabIndexes = [tabIndexes]
+	subcommand = (self._w, 'tab', 'configure') + tuple(tabIndexes)
+	return _doConfigure(self, subcommand, option, kw)
+
+    def tab_names(self, *args):
+        return self.tk.splitlist(self.tk.call((self._w, 'tab', 'names') + args))
+
+    def tab_tearoff(self, tabIndex, newName = None):
+        if newName is None:
+            name = self.tk.call(self._w, 'tab', 'tearoff', tabIndex)
+            return self.nametowidget(name)
+        else:
+            self.tk.call(self._w, 'tab', 'tearoff', tabIndex, newName)
+
+    def view(self):
+        s = self.tk.call(self._w, 'view')
+        return tuple(map(self.tk.getint, self.tk.splitlist(s)))
+    def view_moveto(self, fraction):
+        self.tk.call(self._w, 'view', 'moveto', fraction)
+    def view_scroll(self, number, what):
+        self.tk.call(self._w, 'view', 'scroll', number, what)
Index: /inundation/pmesh/PmwColor.py
===================================================================
--- /inundation/pmesh/PmwColor.py	(revision 1719)
+++ /inundation/pmesh/PmwColor.py	(revision 1719)
@@ -0,0 +1,361 @@
+# Functions for converting colors and modifying the color scheme of
+# an application.
+
+import math
+import string
+import sys
+import Tkinter
+
+_PI = math.pi
+_TWO_PI = _PI * 2
+_THIRD_PI = _PI / 3
+_SIXTH_PI = _PI / 6
+_MAX_RGB = float(256 * 256 - 1) # max size of rgb values returned from Tk
+
+def setscheme(root, background=None, **kw):
+    root = root._root()
+    palette = apply(_calcPalette, (root, background,), kw)
+    for option, value in palette.items():
+	root.option_add('*' + option, value, 'widgetDefault')
+
+def getdefaultpalette(root):
+    # Return the default values of all options, using the defaults
+    # from a few widgets.
+
+    ckbtn = Tkinter.Checkbutton(root)
+    entry = Tkinter.Entry(root)
+    scbar = Tkinter.Scrollbar(root)
+
+    orig = {}
+    orig['activeBackground'] = str(ckbtn.configure('activebackground')[4])
+    orig['activeForeground'] = str(ckbtn.configure('activeforeground')[4])
+    orig['background'] = str(ckbtn.configure('background')[4])
+    orig['disabledForeground'] = str(ckbtn.configure('disabledforeground')[4])
+    orig['foreground'] = str(ckbtn.configure('foreground')[4])
+    orig['highlightBackground'] = str(ckbtn.configure('highlightbackground')[4])
+    orig['highlightColor'] = str(ckbtn.configure('highlightcolor')[4])
+    orig['insertBackground'] = str(entry.configure('insertbackground')[4])
+    orig['selectColor'] = str(ckbtn.configure('selectcolor')[4])
+    orig['selectBackground'] = str(entry.configure('selectbackground')[4])
+    orig['selectForeground'] = str(entry.configure('selectforeground')[4])
+    orig['troughColor'] = str(scbar.configure('troughcolor')[4])
+
+    ckbtn.destroy()
+    entry.destroy()
+    scbar.destroy()
+
+    return orig
+
+#======================================================================
+
+# Functions dealing with brightness, hue, saturation and intensity of colors.
+
+def changebrightness(root, colorName, brightness):
+    # Convert the color name into its hue and back into a color of the
+    # required brightness.
+
+    rgb = name2rgb(root, colorName)
+    hue, saturation, intensity = rgb2hsi(rgb)
+    if saturation == 0.0:
+        hue = None
+    return hue2name(hue, brightness)
+
+def hue2name(hue, brightness = None):
+    # Convert the requested hue and brightness into a color name.  If
+    # hue is None, return a grey of the requested brightness.
+
+    if hue is None:
+	rgb = hsi2rgb(0.0, 0.0, brightness)
+    else:
+	while hue < 0:
+	    hue = hue + _TWO_PI
+	while hue >= _TWO_PI:
+	    hue = hue - _TWO_PI
+
+	rgb = hsi2rgb(hue, 1.0, 1.0)
+	if brightness is not None:
+	    b = rgb2brightness(rgb)
+	    i = 1.0 - (1.0 - brightness) * b
+	    s = bhi2saturation(brightness, hue, i)
+	    rgb = hsi2rgb(hue, s, i)
+
+    return rgb2name(rgb)
+
+def bhi2saturation(brightness, hue, intensity):
+    while hue < 0:
+        hue = hue + _TWO_PI
+    while hue >= _TWO_PI:
+        hue = hue - _TWO_PI
+    hue = hue / _THIRD_PI
+    f = hue - math.floor(hue)
+
+    pp = intensity
+    pq = intensity * f
+    pt = intensity - intensity * f
+    pv = 0
+
+    hue = int(hue)
+    if   hue == 0: rgb = (pv, pt, pp)
+    elif hue == 1: rgb = (pq, pv, pp)
+    elif hue == 2: rgb = (pp, pv, pt)
+    elif hue == 3: rgb = (pp, pq, pv)
+    elif hue == 4: rgb = (pt, pp, pv)
+    elif hue == 5: rgb = (pv, pp, pq)
+
+    return (intensity - brightness) / rgb2brightness(rgb)
+
+def hsi2rgb(hue, saturation, intensity):
+    i = intensity
+    if saturation == 0:
+	rgb = [i, i, i]
+    else:
+	while hue < 0:
+	    hue = hue + _TWO_PI
+	while hue >= _TWO_PI:
+	    hue = hue - _TWO_PI
+	hue = hue / _THIRD_PI
+	f = hue - math.floor(hue)
+	p = i * (1.0 - saturation)
+	q = i * (1.0 - saturation * f)
+	t = i * (1.0 - saturation * (1.0 - f))
+
+	hue = int(hue)
+	if   hue == 0: rgb = [i, t, p]
+	elif hue == 1: rgb = [q, i, p]
+	elif hue == 2: rgb = [p, i, t]
+	elif hue == 3: rgb = [p, q, i]
+	elif hue == 4: rgb = [t, p, i]
+	elif hue == 5: rgb = [i, p, q]
+
+    for index in range(3):
+	val = rgb[index]
+	if val < 0.0:
+	    val = 0.0
+	if val > 1.0:
+	    val = 1.0
+	rgb[index] = val
+
+    return rgb
+
+def average(rgb1, rgb2, fraction):
+    return (
+	rgb2[0] * fraction + rgb1[0] * (1.0 - fraction),
+	rgb2[1] * fraction + rgb1[1] * (1.0 - fraction),
+	rgb2[2] * fraction + rgb1[2] * (1.0 - fraction)
+    )
+
+def rgb2name(rgb):
+    return '#%02x%02x%02x' % \
+        (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))
+
+def rgb2brightness(rgb):
+    # Return the perceived grey level of the color
+    # (0.0 == black, 1.0 == white).
+
+    rf = 0.299
+    gf = 0.587
+    bf = 0.114
+    return rf * rgb[0] + gf * rgb[1] + bf * rgb[2]
+
+def rgb2hsi(rgb):
+    maxc = max(rgb[0], rgb[1], rgb[2])
+    minc = min(rgb[0], rgb[1], rgb[2])
+
+    intensity = maxc
+    if maxc != 0:
+      saturation  = (maxc - minc) / maxc
+    else:
+      saturation = 0.0
+
+    hue = 0.0
+    if saturation != 0.0:
+	c = []
+	for index in range(3):
+	    c.append((maxc - rgb[index]) / (maxc - minc))
+
+	if rgb[0] == maxc:
+	    hue = c[2] - c[1]
+	elif rgb[1] == maxc:
+	    hue = 2 + c[0] - c[2]
+	elif rgb[2] == maxc:
+	    hue = 4 + c[1] - c[0]
+
+	hue = hue * _THIRD_PI
+	if hue < 0.0:
+	    hue = hue + _TWO_PI
+
+    return (hue, saturation, intensity)
+
+def name2rgb(root, colorName, asInt = 0):
+    if colorName[0] == '#':
+	# Extract rgb information from the color name itself, assuming
+	# it is either #rgb, #rrggbb, #rrrgggbbb, or #rrrrggggbbbb
+	# This is useful, since tk may return incorrect rgb values if
+	# the colormap is full - it will return the rbg values of the
+	# closest color available.
+        colorName = colorName[1:]
+        digits = len(colorName) / 3
+        factor = 16 ** (4 - digits)
+        rgb = (
+            string.atoi(colorName[0:digits], 16) * factor,
+            string.atoi(colorName[digits:digits * 2], 16) * factor,
+            string.atoi(colorName[digits * 2:digits * 3], 16) * factor,
+        )
+    else:
+	# We have no choice but to ask Tk what the rgb values are.
+	rgb = root.winfo_rgb(colorName)
+
+    if not asInt:
+        rgb = (rgb[0] / _MAX_RGB, rgb[1] / _MAX_RGB, rgb[2] / _MAX_RGB)
+    return rgb
+
+def _calcPalette(root, background=None, **kw):
+    # Create a map that has the complete new palette.  If some colors
+    # aren't specified, compute them from other colors that are specified.
+    new = {}
+    for key, value in kw.items():
+	new[key] = value
+    if background is not None:
+	new['background'] = background
+    if not new.has_key('background'):
+	raise ValueError, 'must specify a background color'
+
+    if not new.has_key('foreground'):
+	new['foreground'] = 'black'
+
+    bg = name2rgb(root, new['background'])
+    fg = name2rgb(root, new['foreground'])
+
+    for i in ('activeForeground', 'insertBackground', 'selectForeground',
+	    'highlightColor'):
+	if not new.has_key(i):
+	    new[i] = new['foreground']
+
+    if not new.has_key('disabledForeground'):
+	newCol = average(bg, fg, 0.3)
+	new['disabledForeground'] = rgb2name(newCol)
+
+    if not new.has_key('highlightBackground'):
+	new['highlightBackground'] = new['background']
+
+    # Set <lighterBg> to a color that is a little lighter that the
+    # normal background.  To do this, round each color component up by
+    # 9% or 1/3 of the way to full white, whichever is greater.
+    lighterBg = []
+    for i in range(3):
+	lighterBg.append(bg[i])
+	inc1 = lighterBg[i] * 0.09
+	inc2 = (1.0 - lighterBg[i]) / 3
+	if inc1 > inc2:
+	    lighterBg[i] = lighterBg[i] + inc1
+	else:
+	    lighterBg[i] = lighterBg[i] + inc2
+	if lighterBg[i] > 1.0:
+	    lighterBg[i] = 1.0
+
+    # Set <darkerBg> to a color that is a little darker that the
+    # normal background.
+    darkerBg = (bg[0] * 0.9, bg[1] * 0.9, bg[2] * 0.9)
+
+    if not new.has_key('activeBackground'):
+	# If the foreground is dark, pick a light active background.
+	# If the foreground is light, pick a dark active background.
+	# XXX This has been disabled, since it does not look very
+	# good with dark backgrounds. If this is ever fixed, the
+	# selectBackground and troughColor options should also be fixed.
+
+	if rgb2brightness(fg) < 0.5:
+	    new['activeBackground'] = rgb2name(lighterBg)
+	else:
+	    new['activeBackground'] = rgb2name(lighterBg)
+
+    if not new.has_key('selectBackground'):
+	new['selectBackground'] = rgb2name(darkerBg)
+    if not new.has_key('troughColor'):
+	new['troughColor'] = rgb2name(darkerBg)
+    if not new.has_key('selectColor'):
+	new['selectColor'] = 'yellow'
+
+    return new
+
+def spectrum(numColors, correction = 1.0, saturation = 1.0, intensity = 1.0,
+	extraOrange = 1, returnHues = 0):
+    colorList = []
+    division = numColors / 7.0
+    for index in range(numColors):
+	if extraOrange:
+	    if index < 2 * division:
+		hue = index / division
+	    else:
+		hue = 2 + 2 * (index - 2 * division) / division
+	    hue = hue * _SIXTH_PI
+	else:
+	    hue = index * _TWO_PI / numColors
+	if returnHues:
+	    colorList.append(hue)
+	else:
+	    rgb = hsi2rgb(hue, saturation, intensity)
+	    if correction != 1.0:
+		rgb = correct(rgb, correction)
+	    name = rgb2name(rgb)
+	    colorList.append(name)
+    return colorList
+
+def correct(rgb, correction):
+    correction = float(correction)
+    rtn = []
+    for index in range(3):
+	rtn.append((1 - (1 - rgb[index]) ** correction) ** (1 / correction))
+    return rtn
+
+#==============================================================================
+
+def _recolorTree(widget, oldpalette, newcolors):
+    # Change the colors in a widget and its descendants.
+
+    # Change the colors in <widget> and all of its descendants,
+    # according to the <newcolors> dictionary.  It only modifies
+    # colors that have their default values as specified by the
+    # <oldpalette> variable.  The keys of the <newcolors> dictionary
+    # are named after widget configuration options and the values are
+    # the new value for that option.
+
+    for dbOption in newcolors.keys():
+        option = string.lower(dbOption)
+        try:
+            value = str(widget.cget(option))
+        except:
+            continue
+        if oldpalette is None or value == oldpalette[dbOption]:
+            apply(widget.configure, (), {option : newcolors[dbOption]})
+
+    for child in widget.winfo_children():
+       _recolorTree(child, oldpalette, newcolors)
+
+def changecolor(widget, background=None, **kw):
+     root = widget._root()
+     if not hasattr(widget, '_Pmw_oldpalette'):
+	 widget._Pmw_oldpalette = getdefaultpalette(root)
+     newpalette = apply(_calcPalette, (root, background,), kw)
+     _recolorTree(widget, widget._Pmw_oldpalette, newpalette)
+     widget._Pmw_oldpalette = newpalette
+
+def bordercolors(root, colorName):
+    # This is the same method that Tk uses for shadows, in TkpGetShadows.
+
+    lightRGB = []
+    darkRGB = []
+    for value in name2rgb(root, colorName, 1):
+        value40pc = (14 * value) / 10
+        if value40pc > _MAX_RGB:
+            value40pc = _MAX_RGB
+        valueHalfWhite = (_MAX_RGB + value) / 2;
+        lightRGB.append(max(value40pc, valueHalfWhite))
+
+        darkValue = (60 * value) / 100
+        darkRGB.append(darkValue)
+
+    return (
+        '#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
+        '#%04x%04x%04x' % (darkRGB[0], darkRGB[1], darkRGB[2])
+    )
Index: /inundation/pmesh/ProgressBar.py
===================================================================
--- /inundation/pmesh/ProgressBar.py	(revision 1719)
+++ /inundation/pmesh/ProgressBar.py	(revision 1719)
@@ -0,0 +1,80 @@
+"""
+A  basic widget for showing the progress
+being made in a task.
+
+"""
+
+from Tkinter import *
+
+class ProgressBar:
+    def __init__(self, master=None, orientation="horizontal",
+                 min=0, max=100, width=100, height=18,
+                 doLabel=1, appearance="sunken",
+		 fillColor="blue", background="gray",
+                 labelColor="yellow", labelFont="Verdana",
+                 labelText="", labelFormat="%d%%",
+                 value=50, bd=2):
+	# preserve various values
+	self.master=master
+	self.orientation=orientation
+	self.min=min
+	self.max=max
+	self.width=width
+	self.height=height
+	self.doLabel=doLabel
+	self.fillColor=fillColor
+        self.labelFont= labelFont
+	self.labelColor=labelColor
+	self.background=background
+	self.labelText=labelText
+	self.labelFormat=labelFormat
+	self.value=value
+	self.frame=Frame(master, relief=appearance, bd=bd)
+	self.canvas=Canvas(self.frame, height=height, width=width, bd=0,
+                           highlightthickness=0, background=background)
+	self.scale=self.canvas.create_rectangle(0, 0, width, height,
+						fill=fillColor)
+	self.label=self.canvas.create_text(self.canvas.winfo_reqwidth() / 2,
+					   height / 2, text=labelText,
+					   anchor="c", fill=labelColor,
+					   font=self.labelFont)
+	self.update()
+	self.canvas.pack(side='top', fill='x', expand='no')
+
+    def updateProgress(self, newValue, newMax=None):
+        if newMax:
+            self.max = newMax
+        self.value = newValue
+        self.update()
+
+    def update(self):
+	# Trim the values to be between min and max
+	value=self.value
+	if value > self.max:
+	    value = self.max
+	if value < self.min:
+	    value = self.min
+	# Adjust the rectangle
+	if self.orientation == "horizontal":
+	    self.canvas.coords(self.scale, 0, 0,
+		     float(value) / self.max * self.width, self.height)
+	else:
+	    self.canvas.coords(self.scale, 0,
+                     self.height - (float(value) / self.max*self.height),
+		     self.width, self.height)
+	# Now update the colors
+	self.canvas.itemconfig(self.scale, fill=self.fillColor)
+	self.canvas.itemconfig(self.label, fill=self.labelColor)
+	# And update the label
+	if self.doLabel:
+            if value:
+                if value >= 0:
+                    pvalue = int((float(value) / float(self.max)) * 100.0)
+                else:
+                    value = 0
+                self.canvas.itemconfig(self.label, text=self.labelFormat % value)
+            else:
+                self.canvas.itemconfig(self.label, text='')
+	else:
+	    self.canvas.itemconfig(self.label, text=self.labelFormat % self.labelText)
+	self.canvas.update_idletasks()
Index: /inundation/pmesh/README
===================================================================
--- /inundation/pmesh/README	(revision 1719)
+++ /inundation/pmesh/README	(revision 1719)
@@ -0,0 +1,92 @@
+SYSTEM REQUIREMENTS
+ 
+Python, with Pmw must be installed to use pmesh.  Read the
+starting.html file that comes with pmw with regrads to how it is
+installed.(quick install advice: install pmw in the
+python2.2/site-packages dir) (Python and pmw are in the downloads
+directory)
+
+INSTALLATION COMMANDS, FOR LINUX
+
+This is necessary to complite triangle for using pmesh graphically and
+the pmesh.mesh script.
+  	
+To build triangle, as root;
+	python setup.py install
+	
+To build the python package, as a user;
+	python setup.py install --prefix=~
+	
+	This will create a python directory, off the users home directory.
+	Add the path of this directory into your .bashrc 
+	for example;	
+	export PYTHONPATH=$HOME/lib/python2.2/site-packages:"${PYTHONPATH}"
+	another example;
+	export PYTHONPATH=$HOME/lib64/python2.3/site-packages:"${PYTHONPATH}"
+
+INSTALLATION COMMANDS, FOR WINDOWS
+To compile using Microsoft Visual c++,
+	python setup.py install
+
+To compile using mingw,
+	python setup.py build -cmingw32
+	python setup.py install
+
+	Note for this to work mingw has to be installed.  Plus move
+        the file libpython23.a into the python\libs directory.  If
+        'install' does not work move the triang.pyd file manually.
+		
+GENERAL RUNNING	
+To run all tests Harnesses;
+	python TestRunner.py
+	
+To run pmesh;
+	python pmesh.py
+	
+INSTRUCTIONS FOR USING PMESH
+
+Pmesh will let the user select various modes. The current
+allowable modes are vertex or segment.  The mode describes what sort
+of object is added or selected in response to mouse clicks.  When
+changing modes any prior selected objects become deselected.
+
+In general the left mouse button will add an object and the right
+mouse button will select an object.  A selected object can de deleted
+by pressing the the middle mouse button (scroll bar).
+
+NOTES
+I have examples of running triangle in
+ nautilus /home/duncan/MeshGen/triangle_old
+
+CREATING A PMESH EXECUTABLE 
+
+There is a package called py2exe which can take a Python script and
+package it up along with any other scripts it imports and a Python
+interpreter into a single .exe. It will also find any DLLs your script
+depends on and copy them too.
+
+The packaging process:
+
+
+1)Install py2exe. This only needs to be done once on a machine. 
+
+2)Make a static version of Pmw. Pmesh uses a package called Pmw which
+normally dynamically loads pieces of itself as needed. This doesn't
+work with py2exe, so we need to fix it. You'll only need to do this
+once; if you make a change to your script and
+repackage you can skip this step.
+
+  cd to c:\python22\lib\site-packages\Pmw\Pmw_1_1\bin. (Assuming python
+  is installed in the default c:\python22; change the paths if you
+  installed Python somewhere else.
+
+  run "python bundlepmw.py
+  c:\python22\lib\site-packages\Pmw\Pmw_1_1\lib". This will create a
+  file called "Pmw.py" in the current directory.
+
+  copy "Pmw.py" to your main script directory.
+
+  copy "..\lib\PmwBlt.py" and "..\lib\PmwColor.py" to your main script
+  directory, too.
+
+3) Do the command "python exesetup.py py2exe"  This will create a dist directory with the pmesh executable.
Index: /inundation/pmesh/TestRunner.py
===================================================================
--- /inundation/pmesh/TestRunner.py	(revision 1719)
+++ /inundation/pmesh/TestRunner.py	(revision 1719)
@@ -0,0 +1,28 @@
+"""Regression testing framework
+This module will search for scripts in the same directory named
+XYZHarness.py.  Each such script should be a test suite that tests a
+module through PyUnit.  (As of Python 2.1, PyUnit is included in
+the standard library as "unittest".)  This script will aggregate all
+found test suites into one big test suite and run them all at once.
+"""
+
+# Author: Mark Pilgrim
+
+import unittest
+
+import sys, os, re, unittest
+
+def regressionTest():
+    path = os.path.split(sys.argv[0])[0] or os.getcwd()
+    files = os.listdir(path)
+    test = re.compile("Harness.py$", re.IGNORECASE)
+    files = filter(test.search, files)
+    filenameToModuleName = lambda f: os.path.splitext(f)[0]
+    moduleNames = map(filenameToModuleName, files)
+    modules = map(__import__, moduleNames)
+    load = unittest.defaultTestLoader.loadTestsFromModule
+    return unittest.TestSuite(map(load, modules))
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="regressionTest")
+    
Index: /inundation/pmesh/__init__.py
===================================================================
--- /inundation/pmesh/__init__.py	(revision 1719)
+++ /inundation/pmesh/__init__.py	(revision 1719)
@@ -0,0 +1,1 @@
+#nothing
Index: /inundation/pmesh/compile.py
===================================================================
--- /inundation/pmesh/compile.py	(revision 1719)
+++ /inundation/pmesh/compile.py	(revision 1719)
@@ -0,0 +1,327 @@
+"""compile.py - compile Python C-extension
+
+   Commandline usage: 
+     python compile.py <filename>
+
+   Usage from within Python:
+     import compile
+     compile.compile(<filename>,..)
+
+   Ole Nielsen, Oct 2001      
+"""     
+
+ 
+def compile(FNs=None, CC=None, LD = None, SFLAG = None, verbose = 1):
+  """compile(FNs=None, CC=None, LD = None, SFLAG = None):
+  
+     Compile FN(s) using compiler CC (e.g. mpicc), 
+     Loader LD and shared flag SFLAG.
+     If CC is absent use default compiler dependent on platform
+     if LD is absent CC is used.
+     if SFLAG is absent platform default is used
+     FNs can be either one filename or a list of filenames
+     In the latter case, the first will be used to name so file.
+  """
+  
+  
+  ###########################jhdsgfjgdjfg
+  
+  import os, string, sys, types
+  
+  # Input check
+  #
+  assert not FNs is None, "No filename provided"
+
+  if not type(FNs) == types.ListType:
+    FNs = [FNs]
+
+
+  libext = 'so' #Default extension (Unix)
+  libs = ''
+  version = sys.version[:3]
+  
+  # Determine platform and compiler
+  #
+  if sys.platform == 'sunos5':  #Solaris
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'gcc'
+    if LD:
+      loader = LD
+    else:  
+      loader = compiler
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'G'
+      
+  elif sys.platform == 'osf1V5':  #Compaq AlphaServer
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'cc'
+    if LD:
+      loader = LD
+    else:  
+      loader = compiler
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'shared'    
+      
+  elif sys.platform == 'linux2':  #Linux
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'gcc'
+    if LD:
+      loader = LD
+    else:  
+      loader = compiler
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'shared'    
+      
+  elif sys.platform == 'darwin':  #Mac OS X:
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'cc'
+    if LD:
+      loader = LD
+    else:  
+      loader = compiler
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'bundle -flat_namespace -undefined suppress'
+
+  elif sys.platform == 'cygwin':  #Cygwin (compilation same as linux)
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'gcc'
+    if LD:
+      loader = LD
+    else:  
+      loader = compiler
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'shared'
+    libext = 'dll'
+    libs = '/lib/python%s/config/libpython%s.dll.a' %(version,version)
+      
+  elif sys.platform == 'win32':  #Windows
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'gcc'
+    if LD:
+      loader = LD
+    else:  
+      loader = compiler
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'shared'
+    libext = 'dll'
+
+    v = version.replace('.','')
+    dllfilename = 'python%s.dll' %(v)
+    libs = os.path.join(sys.exec_prefix,dllfilename)
+      
+      
+  else:
+    if verbose: print "Unrecognised platform %s - revert to default"\
+                %sys.platform
+    
+    if CC:
+      compiler = CC
+    else:  
+      compiler = 'cc'
+    if LD:
+      loader = LD
+    else:  
+      loader = 'ld'
+    if SFLAG:
+      sharedflag = SFLAG
+    else:  
+      sharedflag = 'G'
+
+            
+  # Find location of include files
+  #
+  if sys.platform == 'win32':  #Windows
+    include = os.path.join(sys.exec_prefix, 'include')    
+  else:  
+    include = os.path.join(os.path.join(sys.exec_prefix, 'include'),
+                           'python'+version)
+
+  # Check existence of Python.h
+  #
+  headerfile = include + os.sep +'Python.h'
+  try:
+    open(headerfile, 'r')
+  except:
+    raise """Did not find Python header file %s.
+    Make sure files for Python C-extensions are installed. 
+    In debian linux, for example, you need to install a
+    package called something like python2.3-dev""" %headerfile
+  
+  
+  # Check filename(s)
+  #
+  object_files = ''
+  for FN in FNs:        
+    root, ext = os.path.splitext(FN)
+    if ext == '':
+      FN = FN + '.c'
+    elif ext.lower() != '.c':
+      raise Exception, "Unrecognised extension: " + FN
+    
+    try:
+      open(FN,'r')
+    except:    
+      raise Exception, "Could not open: " + FN
+
+    if not object_files: root1 = root  #Remember first filename        
+    object_files += root + '.o '  
+  
+  
+    # Compile
+    #
+    s = "%s -c %s -I%s -o %s.o -Wall -O3" %(compiler, FN, include, root)
+
+    if os.name == 'posix' and os.uname()[4] == 'x86_64':
+      #Extra flags for 64 bit architectures
+
+      #FIXME: Which one?
+      #s += ' -fPIC'
+      s += ' -fPIC -m64' 
+      
+      
+    if verbose:
+      print s
+    else:
+      s = s + ' 2> /dev/null' #Suppress errors
+  
+    try:
+      err = os.system(s)
+      if err != 0:
+          raise 'Attempting to compile %s failed - please try manually' %FN 
+    except:
+      raise 'Could not compile %s - please try manually' %FN  
+
+  
+  # Make shared library (*.so or *.dll)
+  
+  s = "%s -%s %s -o %s.%s %s -lm" %(loader, sharedflag, object_files, root1, libext, libs)
+  if verbose:
+    print s
+  else:
+    s = s + ' 2> /dev/null' #Suppress warnings
+  
+  try:  
+    err=os.system(s)
+    if err != 0:	
+	raise 'Atempting to link %s failed - please try manually' %root1     
+  except:
+    raise 'Could not link %s - please try manually' %root1
+    
+
+def can_use_C_extension(filename):
+    """Determine whether specified C-extension
+    can and should be used.
+    """
+
+    from config import use_extensions
+
+    from os.path import splitext
+
+    root, ext = splitext(filename)
+    
+    C=False
+    if use_extensions:
+        try:
+            s = 'import %s' %root
+            #print s
+            exec(s)
+        except:
+            try:
+                open(filename)
+            except:
+                msg = 'C extension %s cannot be opened' %filename
+                print msg                
+            else:    
+                print '------- Trying to compile c-extension %s' %filename
+	    
+                try:
+                    compile(filename)
+                except:
+                    print 'WARNING: Could not compile C-extension %s'\
+                          %filename
+                else:
+                    try:
+                        exec('import %s' %root)
+                    except:
+                        msg = 'C extension %s seems to compile OK, '
+                        msg += 'but it can still not be imported.'
+                        raise msg
+                    else:
+                        C=True
+        else:
+            C=True
+            
+    if not C:
+        pass
+        print 'NOTICE: C-extension %s not used' %filename
+
+    return C
+
+
+
+
+if __name__ == '__main__':
+
+
+  import sys, os
+  from os.path import splitext
+  
+  if len(sys.argv) > 1:
+      files = sys.argv[1:]
+      for filename in files:
+          root, ext = splitext(filename)
+
+          if ext <> '.c':
+	      print 'WARNING (compile.py): Skipping %s. I only compile C-files.' %filename
+      
+  else:  
+      #path = os.path.split(sys.argv[0])[0] or os.getcwd()
+      path = '.'
+      files = os.listdir(path)
+      
+      
+
+  for filename in files:
+      root, ext = splitext(filename)
+
+      if ext == '.c':
+          for x in ['.dll', '.so']:
+              try:
+                  os.remove(root + x)
+              except:
+                  pass
+
+          print '--------------- Trying to compile c-extension %s' %filename
+          try:
+              compile(filename)
+          except:
+              print 'Could not compile C extension %s' %filename            
+          else:
+              print 'C extension %s OK' %filename
+          print    
+        
+
Index: /inundation/pmesh/config.py
===================================================================
--- /inundation/pmesh/config.py	(revision 1719)
+++ /inundation/pmesh/config.py	(revision 1719)
@@ -0,0 +1,76 @@
+"""Module where global model parameters are set
+"""
+
+epsilon = 1.0e-12
+
+default_boundary_tag = 'exterior'
+
+
+time_format = '%d/%m/%y %H:%M:%S'
+
+min_timestep = 1.0e-6 #Should be computed based on geometry
+max_timestep = 1000
+#This is how:
+#Define maximal possible speed in open water v_max, e.g. 500m/s (soundspeed?)
+#Then work out minimal internal distance in mesh r_min and set
+#min_timestep = r_min/v_max 
+# 
+#Max speeds are calculated in the flux function as
+#
+#lambda = v +/- sqrt(gh)
+#
+# so with 500 m/s, h ~ 500^2/g = 2500 m well out of the domain of the
+# shallow water wave equation
+#
+#The actual soundspeed can be as high as 1530m
+#(see http://staff.washington.edu/aganse/public.projects/clustering/clustering.html), but that would only happen with h>225000m in this equation. Why ?
+#The maximal speed we specify is really related to the max speed
+#of surface pertubation
+
+
+
+v_max = 100 #For use in domain_ext.c
+sound_speed = 500
+
+
+max_smallsteps = 50  #Max number of degenerate steps allowed b4 trying first order
+
+g = 9.8        #Gravity
+manning = 0.3 #Manning's friction coefficient
+
+
+eta_w = 3.0e-3 #Wind stress coefficient
+rho_a = 1.2e-3 #Atmospheric density
+rho_w = 1023   #Fluid density [kg/m^3] (rho_w = 1023 for salt water)
+
+        
+
+
+
+
+beta = 0.9  #]0;1] (e.g. 0.9). 1 allows the steepest gradients,
+            #lower values are the more conservative
+            #the limiter being a first order method.
+
+pmesh_filename = '.\\pmesh'
+
+
+import os, sys
+
+if sys.platform == 'win32':
+    data_dir = 'C:\grohm_output'
+else:    
+    data_dir = os.path.expanduser('~'+os.sep+'grohm_output')
+
+
+
+use_extensions = True   #Try to use C-extensions
+#use_extensions = False   #Do not use C-extensions
+
+use_psyco = True  #Use psyco optimisations
+#use_psyco = False  #Do not use psyco optimisations
+
+
+#Specific to shallow water W.E.
+minimum_allowed_height = 1.0e-3 #Water depth below which it is considered to be 0
+
Index: /inundation/pmesh/create_gong.py
===================================================================
--- /inundation/pmesh/create_gong.py	(revision 1719)
+++ /inundation/pmesh/create_gong.py	(revision 1719)
@@ -0,0 +1,96 @@
+
+import os, sys
+sys.path.append('..')
+
+from mesh import *
+from coordinate_transforms.geo_reference import Geo_reference
+
+#-------------------------------------------------------------
+if __name__ == "__main__":
+
+
+    bellambi_northing = 6195000.0
+    north_beach_northing = 6183000.0
+    north_beach_easting = 308500
+    border = 12000.0
+    west = 301000.0 # From the map 
+    inner_east = 310000.0 # From the map 
+    inner_west_south = 303000.0 # From the map 
+    inner_west_north = 307000.0  # From the map 
+    east = 328421.0
+    sea_boundary = (east+inner_east)/2
+    inner_center_easting =  ( inner_east +inner_west_north)/2
+    inner_center_northing = (bellambi_northing + north_beach_northing)/2
+
+    # from 100m dem
+    geo = Geo_reference(xllcorner = inner_center_easting,
+                        yllcorner = inner_center_northing,
+                        zone = 56)
+    print "***********************"
+    print "geo ref", geo
+    print "***********************"
+    
+    m = Mesh(geo_reference=geo)
+    
+    dict = {}
+    dict['points'] = [[west, north_beach_northing - border],  #sw
+                         [west, bellambi_northing + border],  #nw
+                         [sea_boundary,
+                          bellambi_northing + border],  #n_sea_boundary
+                         [east, bellambi_northing + border], #ne
+                         [east, north_beach_northing - border],    #se
+                         [sea_boundary,
+                          north_beach_northing - border]    #s_sea_boundary
+                         ]
+    dict['segments'] = [[0,1],[1,2],[2,3],
+                           [3,4],[4,5],[5,0], # the outer boarder
+                           [2,5]   # the sea_boundary line
+                           ]
+    
+    m.addVertsSegs(dict)
+
+    dict = {}
+    dict['points'] = [
+                         [inner_west_south,north_beach_northing], #sw
+                         [inner_west_north,bellambi_northing],   #nw
+                         [inner_east,bellambi_northing], #ne
+                         [inner_east,north_beach_northing] #se
+                         ]
+    dict['segments'] = [[0,1],[1,2],[2,3],[3,0]] # the inner boarder
+    m.addVertsSegs(dict)
+
+    #Even more inner points
+    flagstaff_x = 308000
+    flagstaff_y = 6189000
+    west_x = 305000
+    west_y = 6185000
+    
+    north_beach_northing = 6183000.0
+    
+    dict = {}
+    dict['points'] = [
+                         [flagstaff_x,flagstaff_y], #n
+                         [west_x,west_y],   #w
+                         [north_beach_easting,north_beach_northing], #s
+                         ]
+    dict['segments'] = [[0,1],[1,2],[2,0]] # the inner boarder
+    m.addVertsSegs(dict)
+    
+    factor = 1000 #low res 10000, high res 1000
+    low = m.addRegionEN(east-0.5, bellambi_northing + border-0.5)
+    low.setMaxArea(100*factor)
+    
+    medium = m.addRegionEN(sea_boundary-0.5, bellambi_northing + border-0.5)
+    medium.setMaxArea(10*factor)
+    
+    high = m.addRegionEN(inner_east-0.5,bellambi_northing-0.5)
+    high.setMaxArea(5*factor) #doing 0.5, fac = 1000 gives degenerate error
+    
+    center = m.addRegionEN(flagstaff_x,flagstaff_y-0.5)
+    center.setMaxArea(0.05*factor) #doing 0.5, fac = 1000 gives degenerate error
+    #m.generateMesh()
+    m.generateMesh("pzq28.0za1000000a") 
+    
+    m.export_mesh_file("wollongong_4regions.msh")
+    #m.export_mesh_file("wollongong_highres_a28.tsh")
+    
Index: /inundation/pmesh/cursornames.py
===================================================================
--- /inundation/pmesh/cursornames.py	(revision 1719)
+++ /inundation/pmesh/cursornames.py	(revision 1719)
@@ -0,0 +1,8 @@
+TLC = 'top_left_corner'
+TRC = 'top_right_corner'
+BLC = 'bottom_left_corner'
+BRC = 'bottom_right_corner'
+TS  = 'top_side'
+RS  = 'right_side'
+LS  = 'left_side'
+BS  = 'bottom_side'
Index: /inundation/pmesh/documentation/issues.txt
===================================================================
--- /inundation/pmesh/documentation/issues.txt	(revision 1719)
+++ /inundation/pmesh/documentation/issues.txt	(revision 1719)
@@ -0,0 +1,311 @@
+CURRENT PMESH ISSUES
+
+OPEN - issues that have not beeen resolved
+ 
+Issue: On the cluster, if storm_surge/pmesh is in the pythonpath (and
+not storm_surge?), pmesh.mesh fails by trying to load pmesh
+visualisation stuff.  Pmesh shouldn't be trying to load anyhow.
+
+Issue: crash if file>filter alpha boundary if there is no alpha
+boundary.
+
+Issue: Add export seg outline file back into pmesh
+
+Issue: triangles_to_polySet (outlining the current set) sometimes
+duplicates vertices.  Now that is fixed, but segments that overlap (ie
+one big line that gets little ones drawn on it) break triangle.
+
+Suggested action: make a dictionary of Hough transforms (parametric
+lines i.e. angle, x-intercept) of the segments. Check any that take up
+the same space. Fix overlapping lines.
+
+Issue: Red mesh selected for refinement seems to be 'behind' the green
+mesh after selecting all then clearing the selection.
+Importance: low
+Suggested Action: Fixed BUT now it draws the greens behind the reds.
+It is much less obvious now though.
+The only way to fix it for good is to colour in the triangles - that
+is the only way to guarantee you know what each triangle is.
+
+
+Issue:  Ole thought that the regional mesh max areas were incorrect
+when a .tsh file was loaded.  Maybe it was that the overall max area
+is not saved.  It's hard coded in MeshGenDialog.
+Importance: Medium
+Suggested Action: Fix when .tsh goes NETCDF
+
+Issue: Catch error if .tsh file is a bad format.
+suggestion: quick and dirty try - except
+Importance: Medium
+
+Issue (new feature): Have visualisation of verts be turned on and
+off.  This will speed things up.
+Importance: low
+
+Issue (new feature): Hve .tsh as a netscf fie, instead of an ascii
+file.
+Importance: Medium
+
+Issue: When a file is saved as and then saved later, sometimes the
+file name is not known.   Is it that 'clear' is removing the name
+(which it should). 
+
+Issue:A .tsh file loaded into pmesh after smoothing will not have the
+outline info, since this is removed when smoothing.  If this is a
+problem, refactor loadASCII so it can write the outline as well.
+Suggested Action: Document use
+
+Issue:vertices at the bottom of the canvas can be hard to visualise,
+since you don't know if all vertices are on the screen.  
+Suggested Action: have a 5% border
+Importance: low to not do
+
+Issue: The mesh produced by triangle isn't of high quality near a low
+angle formed by two user segments.
+Reason:triangle
+Tried:  Tried the triangle flags L,i,V (for info).  Using box.poly on
+nautilus.  Couldn't get a better quality mesh.
+
+Issue: Get the naming right for mesh/triangulation/user/generated.
+How about "outline + triangulation = mesh"
+Importance: Medium -low
+
+Issue: S/W - too many methods with the same name on differnent classes
+eg editWindow.  Makes things confusing. 
+Importance: low
+
+Issue: Normalise seems broken - what is it doing to the attributes?.  
+Suggested Action: remove!
+
+Bug: Calling 'Generate Mesh' on empty document or If there are no
+enclosed regions will cause trianglulation to crash pmesh.
+Importance: low - not do, but document
+Suggested Action: Catch them (with try) and do nothing
+notes:   Fixing the 'Generate Mesh' crash is a P3 requirement.
+
+The zoom in/out is jittery
+
+The initial mesh window coordinates are in the fourth quadrant.
+
+Deleting a selected object is done with the scroll bar, not the 
+delete key. 
+
+Issue: triangle calculates incorrect attribute values, some of the time.
+Importance: Medium
+Suggested Action: Preprocess data to smoth it out.
+
+Issue: pmesh can not be automatically installed in windows and linux,
+triangle doesn't compile easily.
+Installs best in Linux.  
+Importance: Low
+Suggested Action: delve into setup more so it works with a variety of
+compilers.
+
+Issue: two vertices can be ontop of one another
+Importance: Low
+Suggested Action: See how it goes when used.  The cost of checking
+probably doesn't outwiegh the benifit.
+action taken: When loading an xya file duplicate vertices are removed. 
+
+
+Issue: When calculating mesh, have the pointer turn into an hour glass
+Importance: Low
+
+****************
+CLOSED - issues that have  beeen resolved
+************
+Issue: If some triangles have tags and others don't then the data
+structure (a list of lists) is dificult to use in Numeric.
+Solution -  use a list structure, instead of list of lists 
+Closed -  used a list structure, instead of list of lists 
+ 
+Issue (Ole): I couldn't get pmesh to write mesh in the .msh format
+Importance: Mid
+Suggested action: Either 
+    1:  Equip scripted mesh generation with functionality for tagging
+    2:  Allow pmesh to store as msh
+    3:  Have a tsh2msh converter
+Closed: Couldn't be reproduced. suggested action 2 has already been
+implemented.  More investigation required before calling an issue an issue..
+
+Issue: After selecting all for refinement then clearing the selection
+segments (blue lines) can not be selected.
+Importance: High
+Suggested Action:Fixed
+Reason: 
+
+Issue: Refined triangles don't have the correct region information.
+Importance: High
+Suggested Action: Fixed
+Reason: I don't think 'refine' fixes triangle attributes
+
+
+Issue: Pmesh won't save a refined mesh.
+Importance: High
+Reason: Refine_triangle.getNewVertex was not appending to mesh.meshVertices,
+        nor was it assigning an index to the vertex.
+
+Issue When starting generate mesh, the cursor doesn't start in the
+first box.
+Importance: Low
+Action: fixed
+
+Issue: Why is triangle att's set as a list and returned as a list of
+lists, when loading from a .tsh file?  Note: a change of this might
+also affect the 'triangle' glue code.
+Note: This also happens to triangle atts! (?)
+action: triangle att's set as a list of lists
+
+Issue:Implememt the following with mesh gen minimum angle, 0 - 32
+nothing;32-40 warning message, may not converge; 40 - error message 
+Importance: Low
+Action: done
+
+Issue: Middle magnifying glass icon does not describe what it will do.
+Importance: Low Low
+action: too unimportant to do
+
+Issue:  Maybe triangle doesn't normalise meshes away from the origin,
+resulting in round-off error.
+Importance: Low Low
+Suggested Action: closed since this didn;t seem to be a problem.
+
+Issue:hull.exe generates files that it doesn't clean up eg called
+s1f8, se4, etc. These seem to turn up in a root directory
+Importance: Low
+
+Issue: Hull.exe (alpha shape) - gives an assertion error if points are
+too close together?.  
+Importance: Low
+Suggested Action: when using the results from hull.exe, check if it
+has given any results and throw a friendly message if there are no results.
+
+Issue: When adding segments, vertices are hard to select (with the mouse).
+
+Issue: Calling autoSegment when not in the pmesh directory will not
+work, since it calls the hull.exe, assuming it's in the current dir 
+Importance: medium
+Suggested Action: Have the current dir change to the pmesh dir , run
+hull, then change back
+action: contractor is making alpha shape in python
+
+Issue: In the shallow_water functions for pmesh, make the
+volume.interpolate_cons_quantitie call more automatic
+
+Issue: Need tests for pmesh functions in  shallow_water.py
+
+Issue: If there is a large scale grid, how many triangles can be
+generated?  with a normalised grid there's a limit of 22674.
+Using a 100 * 100 domain, a max triangle area of 0.01 produced
+1,048,576 triangles.
+
+Issue: Internal boudaries get an empty string as tag (per default?)
+Background: The notion of defaiults values for internal boundaries was removed, but this one seems to have taken its place. The following hack will 
+remove the tag: 
+  #for b in domain.boundary.keys():
+  #    if domain.boundary[b] == '':
+  #        del domain.boundary[b]
+  #
+  #domain = Domain(domain.coordinates, domain.triangles, domain.boundary)
+Importance: high
+
+
+Issue: The default region button cannot be selected after loading a file.
+Reason: Unknown.  
+Importance: Low
+
+Issue: The vertices were being sorted when loading .xya files
+Time to complete : 5hrs
+Issue: In load ascii all text reading of lines using find must ltrim
+beforehand
+
+Issue: pmesh -when loading/saving the dir should be the dir where the
+file lives.  Not always the current dir. 
+
+Issue: Tags for specifying boundary conditions should be arbitrary
+Background: Domain has the functionality for using arbitrary keys
+when associating boundary objects to boundary values.
+Triangle, which pmesh is based on, uses integers only.
+It would be nice to have mnemonic names for boundaries throughout.
+Importance: Mid
+Status: Unresolved
+
+Issue: Calling save the first time seemingly does nothing
+Importance: Low
+Suggested Action: Call save_as when save is clicked the first time.
+
+
+Bug: Calling Zoom functions on empty document 
+causes an AttributeError Exceptions.
+Importance: Medium
+Suggested Action: Catch them (with try) and do nothing
+notes: the zoom crash is not reproducable.  Fixing the 'Generate Mesh' crash
+is a P3 requirement.
+
+When doing mesh generation, no default values (min angle, max area)
+are shown. - Low priority for Chris
+
+
+Cursor shape changes when it shouldn't.
+
+Issue: Help message and status message are the same.  Remove one
+Importance: Low
+Suggested Action: Remove the status info
+
+
+Issue: Selecting nodes require that boundary is selected precisely.
+Importance: High
+Suggested Action: Allow click in interior of square.
+
+Bug: Entering negative value for area is allowed.
+Importance: Medium
+Suggested Action: Check input and return error message
+
+
+Issue: Minimum angle needs a specification of what interval is allowed.
+It seems to pick a default value if angle is out of bounds.
+Action: angles <0.0, >90.0 not allowed 
+
+Bug: Entering zero for area causes crash.
+Importance: Medium
+Suggested Action: Check input and return normal error message
+
+
+Issue: Re-generating grids goes slower and slower when repeated.
+Importance: Low
+Suggested Action: Check that old grid gets removed from memory.
+Action taken: cleaned up how visualised objects are handled
+
+
+Issue: nothing says what mode(seg or vertex) pmesh is in
+Importance: Low
+Action taken: Have the buttons stay 'down', to depict mode.
+
+  
+
+Issue: Meaning of 1 x zoom is not intuitive. 
+Related issue: Absolute scale should be reported in status bar
+Importance: Medium
+ Action taken : Zoom x 1  gives a zoom corresponding 
+to the opening screen.
+
+Vertices and segments that have a triangle on top of them are hard to 
+select/see.  Ole suggested that there be the capability to view/hide the mesh.
+ Action taken :put segs/vertices on top
+
+
+NOTES
+The arrow and postscript bottons currently do nothing.
+
+
+-----------------------------------------------
+NEW REQUIREMENTS
+
+Introduce 'eraser tool'. When selected left clicking any object will remove it.
+
+Allow generated grid to be cleared (or hidden).
+
+
+
+CVS INFORMATION FOR THIS FILE
+$Source: /var/lib/cvs/pmesh/documentation/issues.txt,v $
Index: /inundation/pmesh/documentation/mesh_gen_SRS.txt
===================================================================
--- /inundation/pmesh/documentation/mesh_gen_SRS.txt	(revision 1719)
+++ /inundation/pmesh/documentation/mesh_gen_SRS.txt	(revision 1719)
@@ -0,0 +1,331 @@
+Pmesh Software Requirements Specification
+By Duncan Gray
+
+$Revision: 1.29 $
+$Date: 2004/08/13 08:31:15 $
+
+INTRODUCTION
+
+The software, called pmesh, will produce a triangulation based on
+initial vertices and boundaries.  The produced triangulation can be
+used by an evolution code to solve storm surge problems using the Finite
+Volume Method.
+
+
+The requirements in this document describe the software features
+needed to produce triangulations useful for Geohazards.
+
+OVERALL DESCRIPTION 
+
+Pmesh is a graphical user interface (GUI) for triangle (a mesh
+generating program developed by J. R. Shewchuk) which is a mesh
+generation program. It will allow vertices (points) and segments
+(straight lines between the vertices) to be added to a canvas.
+
+
+
+Pmesh will let the user select various modes. The initial
+allowable modes will be vertex or segment.  The mode describes what sort
+of object is added or selected in response to mouse clicks.  When
+changing modes any prior selected objects become deselected.
+ 
+In general the left mouse button will add an object and the right mouse
+button will select an object.  A selected object can de deleted by
+pressing the delete key.  Additionaly, by pressing the middle mouse
+button (scroll bar) the attributes of a selected object can be edited.
+
+The graphical objects are of two types; user or generated.  User
+objects are vertices and segments inputted by the user.  These are
+used by triangle when generating a mesh.
+
+ The generated objects are vertices, segments and triangles of the
+triangulation.  Only the triangles are graphically displayed.
+Generated objects can not be modified by the user. To modify the
+generated triangulation edit the user objects and generate a new
+trianglulation.
+
+
+DEFINITIONS
+
+Segments are edges whose presence in the triangulation is constraints.
+Segments can be used to define the boundaries of a problem.
+
+A triangulation is the triangles, vertices and segments generated by
+triangle.
+
+A mesh is all the triangle elements .e.g. the vertices, segments and
+triangulation.
+
+A Planar Straight Line Graph (PSLG) is a set of vertices and segments.
+Segments are simply edges, whose endpoints are vertices in the PSLG.
+Segments may join each other only at their endpoints and must not intersect.
+
+
+REQUIREMENT SECTION
+
+Introduction
+
+Each requirement has a priority (e.g. P1), with 1 being the highest
+and 10 the lowest.  The priority number is given at the end of each
+requirement.
+
+IMPLIMENTED
+
+Requirements
+
+- When a mesh is loaded  all of the mesh will fit in the application
+  window - P1 DONE
+  
+- If some of the mesh is not visible scroll bars are used to make
+  the canvas visible - P1 DONE
+
+- Have ability to zoom in by *2 on the mesh and zoom out, by
+  *0.5. -P1 DONE 
+ 
+- add user vertices, positioning by mouse - P1 DONE
+		     , positioning by entering x,y values - P1 DONE
+
+- add user segments, vertices selection by mouse - P1 DONE
+
+- be able to select a user vertex. -P2 DONE
+
+- be able to select a user segment. -P2 DONE
+
+- to be able to delete selected user objects. -P2 DONE
+
+- be able to delete an entire mesh. - P2 DONE
+  
+- To be able to load and save meshes as a python pickled object. -P2 DONE
+
+- access to triangle mesh generator,
+	  user can select min angle, max area - P1 DONE
+	  user can select rough number of triangles to generate - P3
+	  DONE
+	  
+- The screen should reflect the current generated triangulation -P2 DONE
+
+- Have 'Zoom' button that positions all of the mesh in the viewing
+  window - P3 DONE  
+
+- Be able to automatically generate segments that bound all of the
+user vertices, but not a convex hull bound.  Specifically, be able to
+apply an alpha shape around a set of vertices. - P3 - DONE
+ 
+- specify a region by selecting a point bounded by segments. - P30 - DONE
+
+- specify an attribute to a region. - P30 - DONE
+
+- specify a hole in the mesh by selecting a point bounded by
+  segments. - P30 - DONE
+
+
+- write an ascii file that outputs - P30 - DONE
+# of vertices
+[x,y]
+# of triangles
+[vert1 #, vert2 #, vert3 #, adjoining triangle 1, adjoining triangle
+2, adjoining triangle 3, attributes of region ]
+if there is no adjoining triangle, give -1 as the adjoining triangle.
+
+
+- specify how much vertex attribute information can be added to the mesh. -P40
+  - eg one attribute per vertex, two attributes per vertex
+- specify the default attribute vertex value - P40
+
+- edit segment attribute.  Can only select integers -P40 - DONE
+
+
+- load the following user vertex file layout 
+  - First row {"Lat", "Long", "Depth"}
+  - Subsiquent rows x y positions with 1 attribute
+  - Initially, load the file type, ditching the attribute info - P3 DONE
+  - Use the attribute info - P60 - DONE
+
+- Have an ascii export/import file format, that fully specifies a mesh 
+  - Add more output to the current export, so a mesh is fully specified
+  - Add an import of the exported format   - P60 - Done
+  
+- Print out the triangulation as a postscript file - P80 - DONE
+
+- Be able to tag boundaries with strings. - DONE
+ 
+- Have a warning prompt when saving a mesh where the generated mesh is
+  different from the user mesh - eg boundary tags that aren't carried
+  thru. Warning ~"Mesh not generated after changes.  Generate mesh?  "
+  - cancel, don't gen, don't save.  Yes - generate mesh, go to save
+  screen.  No - goto save screen.  To implement this need to know when
+  the user has done a change, and the mesh hasn't been generated.  If
+  there is no generated mesh do not prompt. - DONE
+
+access to triangle mesh generator,
+	  user can select  max area in region - P80 - DONE
+ 
+- .xya file, which has vertex information, will have headers naming
+the attributes in the first row.  The headers will be strings
+representing a label for the attribute information.  This information
+will be space deliminated.  There will be no ordering imposed.  The
+name, column index mapping will be in the .tsh file.
+      - x and y headings are manitory. - not done, requirements changed
+      - Missing fields will cause an error during loading of the xya file.
+
+ 
+ NOT IMPLIMENTED
+- show file name in title bar
+ 
+- Have a divide all max areas by value x function. - P80
+
+- triang.genMesh - get triangle compiled automatically. 
+
+- able to specify a 'circular' region by specifying center, radius and
+  number of number of segments. (The functionality is there as a hack.
+   need to add gui etc.) - P100
+   
+- Display mouse point position [x,y] - P60
+
+- Calculate total area enclosed (maybe a buton?) -P80
+	  (this requirement GUI aspect needs to be fleshed out with Chris)
+	  
+- Load in a segment attribute to boundary mapping.  ie segment
+  attribute is 'seen' as a boundary, such as a 0 flux boundary and
+  this is interanally represented as an interger, as defined by the
+  mapping. - P100 
+  
+- Select segment attribute from an enumerated list of boundaries. - P100
+  
+- Select segment attribute from an enumerated list of boundaries and
+  can also input values of constants described in the boundary. - P100
+
+- Select objects  - Be able to select multiple objects.  Eg select
+  many segments and mark with one attribute -P100
+
+- Be able to select objects by "mouse dragging" a rectangle. (A
+  rectangle whos size and position is controlled by the mouse)
+  Everything within the rectangle is selected.
+  
+- Move user vertices  - can initially be  done by
+  adding/deleting vertices and segments. -P100 [should we have this feature?]
+
+- specify a maximum area for each triangle, based on current mesh
+  (this is refinement of the current mesh) - P100
+
+- able to specify a 'circular' region by specifying center, radius and
+  number of number of segments. - P100
+  
+-Log window.  Have a separate window that displays log information.
+This window can be turned on or off. - P100
+
+- Log information of the generation mesh process.  - P100
+  - max area
+  - minimum angle
+  [Other information to be determined ] 
+
+- reduce mesh generation crashing by only mesh generating if all of the
+vertices are bounded by segments. - P100
+
+- Set the initial mesh co-ordinate to origin (0,0) at canvas bottom left and
+  (100,100)at canvas top right.
+
+- Have info with the mesh, such as #of triangles, min angle, max area
+	  (this requirement GUI aspect needs to be fleshed out with Chris)
+	  
+- Put postscript/clear/close buttons as a menu, not at the bottom of
+  pmesh
+  
+- assume the scale is metres, important for the programs using the
+mesh.  Maybe in add vertex have the scale?
+
+- be able to add two .tsh files together.
+
+- run triangle as a seperate thread, so if it's not converging it can
+be stopped. - P100
+ 
+-  
+__________________
+ 
+The information below describe the various colours for drawn objects,
+given the objects state.
+User Vertex, not selected  - small black rectangle (to represent a point)
+	     selected      - small red rectangle
+	     selected when adding a segment - small orange rectangle
+User Segment, not selected  - blue line
+	     selected      - red line
+	     
+Computer Vertices and Segments will not be drawn
+Computer Triangle,  not selected  - green triangle 
+	     selected      - red triangle (can be selected to add max
+	     area value when refining triangulations)
+_________________________
+
+
+DEVELOPMENT
+
+The pmesh software will be developed using an iterative approach, with
+it being "released" several times and each release having increased
+functionality. The first iteration will be a minimal system.
+Requirements to be delivered in each iteration are described below.
+
+Currently four iterations are planned.
+Each requirement is associated with an iteration, via the requirement's
+priority, represented by a P number. P1, P2, P3 etc.
+
+ 
+1st iteration P1 & P2 - basic framework, loading and saving of meshes
+2nd iteration P3      - specifying  # of triangles in mesh
+3rd iteration P30     - specify holes, regions and having attributes
+			for regions  
+4rd iteration P40     - attribute and boundary manipulation
+			 
+5th iteration P60     - increased GUI functionality
+
+6th iteration P80     - region selection, selection of different max
+			 triangle areas based on region, segments
+			automatically added
+Future iterations P100    
+__________________________
+
+NOTES
+
+How to speed things up.
+Use numericpy to build the data structures and then pass them straight
+to the triangle.o (This is assuming the numericpy data structures are
+in a c format.) - Don't lose the OO structure though. 
+
+preallocate lists, instead of calling c repeditively
+_______
+Questions
+What format will the initial data in? Chris is going to find an inital
+format, from previous work.
+
+How will data be taken in future? ie do we have datasets that we will
+convert or will we hand generate initial data sets?
+ 
+What s/w package takes care of the boundary conditions?
+[this seems to be an open issue still.]
+
+Things to work out with Chris and Steve: 
+How can the quality of the meshes be tested? Chris believes triangle
+diagnostices should provide this information.
+
+Will we need this?
+  -L  Do not use diametral lenses to determine whether subsegments are
+        encroached; use diametral circles instead (as in Ruppert's
+        algorithm).  Use this switch if you want all triangles in the mesh
+        to be Delaunay, and not just constrained Delaunay; or if you want
+        to ensure that all Voronoi vertices lie within the triangulation.
+        (Applications such as some finite volume methods may have this
+        requirement.)  This switch may increase the number of vertices in
+        the mesh to meet these constraints.
+ ________________________________________________________________
+
+REQUIREMENT ISSUES
+
+ - if a vertex with attribute info is moved, the attribute info does
+ not change.  This will not reflect the actual environment. Option
+ 1)The user can enter correct attribute data
+  
+ 2) the s/w can interpolate, based on the original vertex data.
+ 
+ Currently suggesting option 1 - Duncan and Chris.
+ 
+CVS INFORMATION FOR THIS FILE
+$Source: /var/lib/cvs/pmesh/documentation/mesh_gen_SRS.txt,v $
Index: /inundation/pmesh/exesetup.py
===================================================================
--- /inundation/pmesh/exesetup.py	(revision 1719)
+++ /inundation/pmesh/exesetup.py	(revision 1719)
@@ -0,0 +1,24 @@
+# setup.py
+#run with "python exesetup.py py2exe"
+from distutils.core import setup
+import py2exe
+      
+setup(console=["pmesh.py"],
+      data_files=[("icons",
+                   ["icons/addVertex.gif",
+                    "icons/autoSeg.gif",
+                    "icons/edit.gif",
+                    "icons/hole.gif",
+                    "icons/joinVer.gif",
+                    "icons/meshGen.gif",
+                    "icons/pointer.gif",
+                    "icons/default.gif",
+                    "icons/region.gif",
+                    "icons/segment.gif",
+                    "icons/sep.gif",
+                    "icons/vertex.gif",
+                    "icons/zoom0.5.gif",
+                    "icons/zoom1.0.gif",
+                    "icons/zoom2.gif",
+                    "icons/zoomToMesh.gif"]),
+                  (".",["./hull.exe"])])
Index: /inundation/pmesh/load_mesh/__init__.py
===================================================================
--- /inundation/pmesh/load_mesh/__init__.py	(revision 1719)
+++ /inundation/pmesh/load_mesh/__init__.py	(revision 1719)
@@ -0,0 +1,1 @@
+#nothing
Index: /inundation/pmesh/load_mesh/loadASCII.py
===================================================================
--- /inundation/pmesh/load_mesh/loadASCII.py	(revision 1719)
+++ /inundation/pmesh/load_mesh/loadASCII.py	(revision 1719)
@@ -0,0 +1,1315 @@
+"""
+The format for a .xya file is:
+1st line:     [attribute names]
+other lines:  x y [attributes]
+
+for example:
+elevation, friction
+0.6, 0.7, 4.9, 0.3
+1.9, 2.8, 5, 0.3
+2.7, 2.4, 5.2, 0.3
+
+The first two columns are always implicitly assumed to be x, y coordinates.
+Use the same delimiter for the attribute names and the data
+
+An xya file can optionally end with
+#geo reference
+56
+466600.0
+8644444.0
+
+When the 1st # is the zone,
+2nd # the xllcorner and 
+3rd # the yllcorner
+The format for a Points dictionary is:
+
+  ['pointlist'] a 2 column array describing points. 1st column x, 2nd column y.
+  ['attributelist'], a dictionary of 1D arrays, representing attribute values
+  at the point.  The dictionary key is the attribute header.
+    eg
+    dic['pointlist'] = [[1.0,2.0],[3.0,5.0]]
+    dic['attributelist']['elevation'] = [[7.0,5.0]
+
+    
+    The dict format for IO with mesh files is;
+    (the triangulation)
+    vertices: [[x1,y1],[x2,y2],...] (lists of doubles)
+    vertex_attributes: [[a11,a12,...],[a21,a22],...] (lists of doubles)
+    vertex_attribute_titles:[A1Title, A2Title ...] (A list of strings)
+    segments: [[v1,v2],[v3,v4],...] (lists of integers) 
+    segment_tags : [tag,tag,...] list of strings
+    triangles : [(v1,v2,v3), (v4,v5,v6),....] lists of points
+    triangle_tags: [s1,s2,...] A list of list of strings (probably not neccecary.  a list of string should be ok)
+    triangle_neighbors: [[t1,t2,t3], [t4,t5,t6],..] lists of triangles
+        
+    (the outline)   
+    points: [[x1,y1],[x2,y2],...] (lists of doubles)
+    point_attributes: [[a11,a12,...],[a21,a22],...] (lists of doubles)
+    outline_segments: [[point1,point2],[p3,p4],...] (lists of integers) 
+    outline_segment_tags : [tag1,tag2,...] list of strings
+    holes : [[x1,y1],...](List of doubles, one inside each hole region)
+    regions : [ [x1,y1],...] (List of 4 doubles)
+    region_tags : [tag1,tag2,...] (list of strings)
+    region_max_areas: [ma1,ma2,...] (A list of doubles)
+    {Convension: A -ve max area means no max area}
+
+    Points files are .xya for ascii and .pts for NetCDF
+    Mesh files are .tsh for ascii and .msh for NetCDF
+
+    Note: point att's have no titles - that's currently ok, since least
+    squares adds the point info to vertices, and they have titles
+
+    The format for a .tsh file is (FIXME update this)
+    
+    First line:  <# of vertices> <# of attributes>
+    Following lines:  <vertex #> <x> <y> [attributes]
+    One line:  <# of triangles> 
+    Following lines:  <triangle #> <vertex #>  <vertex #> <vertex #> <neigbouring triangle #> <neigbouring triangle #> <neigbouring triangle #> [attribute of region]
+    One line:  <# of segments> 
+    Following lines:  <segment #> <vertex #>  <vertex #> [boundary tag]
+"""
+##FIXME (DSG-DSG) Is the dict format mentioned above a list of a numeric array?
+#  Needs to be defined
+##FIXME (DSG-DSG) if the ascii file isn't .xya give a better error message.
+
+
+from string import  find, rfind
+from Numeric import array, Float, Int16, Int32, Character,reshape, concatenate, take
+from os.path import splitext
+
+from coordinate_transforms.geo_reference import Geo_reference,TITLE
+
+import exceptions
+class TitleAmountError(exceptions.Exception): pass
+
+NOMAXAREA=-999
+
+# This is used by pmesh
+def import_mesh_file(ofile):
+    """
+    read a mesh file, either .tsh or .msh
+    
+    Note: will throw an IOError if it can't load the file.
+    Catch these!
+    """
+    try:
+        if ofile[-4:]== ".tsh":
+            dict = _read_tsh_file(ofile)
+        elif ofile[-4:]== ".msh":
+            dict = _read_msh_file(ofile)
+        else:      
+            msg = 'Extension %s is unknown' %ofile[-4:]
+            raise IOError, msg
+    except (SyntaxError,IndexError, ValueError): #FIXME No test for ValueError
+            msg = 'File could not be opened'
+            raise IOError, msg  
+    return dict
+
+
+def export_mesh_file(ofile,mesh_dict):
+    """
+    write a file, ofile, with the format
+    
+    First line:  <# of vertices> <# of attributes>
+    Following lines:  <vertex #> <x> <y> [attributes]
+    One line:  <# of triangles> 
+    Following lines:  <triangle #> <vertex #>  <vertex #> <vertex #> <neigbouring triangle #> <neigbouring triangle #> <neigbouring triangle #> [attribute of region]
+    One line:  <# of segments> 
+    Following lines:  <segment #> <vertex #>  <vertex #> [boundary tag]
+    """
+    #FIXME DSG-anyone: automate 
+    if not mesh_dict.has_key('points'):
+        mesh_dict['points'] = []
+    if not mesh_dict.has_key('point_attributes'):
+        mesh_dict['point_attributes'] = []
+    if not mesh_dict.has_key('outline_segments'):
+        mesh_dict['outline_segments'] = []
+    if not mesh_dict.has_key('outline_segment_tags'):
+        mesh_dict['outline_segment_tags'] = []
+    if not mesh_dict.has_key('holes'):
+        mesh_dict['holes'] = []
+    if not mesh_dict.has_key('regions'):
+        mesh_dict['regions'] = []
+        
+    if not mesh_dict.has_key('region_tags'):
+        mesh_dict['region_tags'] = []
+    if not mesh_dict.has_key('region_max_areas'):
+        mesh_dict['region_max_areas'] = []
+    if not mesh_dict.has_key('vertices'):
+        mesh_dict['vertices'] = []
+    if not mesh_dict.has_key('vertex_attributes'):
+        mesh_dict['vertex_attributes'] = []
+    if not mesh_dict.has_key('vertex_attribute_titles'):
+        mesh_dict['vertex_attribute_titles'] = []
+    if not mesh_dict.has_key('segments'):
+        mesh_dict['segments'] = []
+    if not mesh_dict.has_key('segment_tags'):
+        mesh_dict['segment_tags'] = []
+    if not mesh_dict.has_key('triangles'):
+        mesh_dict['triangles'] = []
+    if not mesh_dict.has_key('triangle_tags'):
+        mesh_dict['triangle_tags'] = []
+    if not mesh_dict.has_key('triangle_neighbors'):
+        mesh_dict['triangle_neighbors'] = []
+    #print "DSG************"
+    #print "mesh_dict",mesh_dict 
+    #print "DSG************"
+    
+    if (ofile[-4:] == ".tsh"):
+        _write_tsh_file(ofile,mesh_dict)
+    elif (ofile[-4:] == ".msh"):
+        _write_msh_file(ofile, mesh_dict)
+    else:
+        msg = 'Unknown file type %s ' %ofile
+        raise IOError, msg 
+
+def _read_tsh_file(ofile):
+    """
+    Read the text file format for meshes
+    """
+    fd = open(ofile,'r')
+    dict = _read_triangulation(fd)
+    dict_mesh = _read_outline(fd)
+    for element in dict_mesh.keys():
+        dict[element] = dict_mesh[element]
+    fd.close()
+    return dict
+
+    
+def _read_triangulation(fd):
+    """
+    Read the generated triangulation, NOT the outline. 
+    """
+    delimiter = " "
+    ######### loading the point info
+    line = fd.readline()
+    #print line
+    fragments = line.split()
+    #for fragment in fragments:
+    #print fragment
+    if fragments ==[]:
+        NumOfVertices = 0
+        NumOfVertAttributes = 0
+    else:
+        NumOfVertices = fragments[0]
+        NumOfVertAttributes = fragments[1]
+    points = []
+    pointattributes = []
+    for index in range(int(NumOfVertices)):        
+        #print index
+        fragments = fd.readline().split()
+        #print fragments
+        fragments.pop(0) #pop off the index
+        
+        # pop the x y off so we're left with a list of attributes       
+        vert = [float(fragments.pop(0)),float(fragments.pop(0))]
+        points.append(vert)
+        apointattributes  = []
+        #print fragments
+        for fragment in fragments:
+            apointattributes.append(float(fragment))
+        pointattributes.append(apointattributes)
+        
+    ######### loading the point title info
+    line = fd.readline()
+    #print "point title comments",line
+    vertTitle = []
+    for index in range(int(NumOfVertAttributes)):        
+        #print index
+        fragments = fd.readline().strip() 
+        vertTitle.append(fragments)
+        #print vertTitle
+        
+    ######### loading the triangle info
+    line = fd.readline()
+    #print "triangle comments",line
+    fragments = line.split()
+    #for fragment in fragments:
+    #    print fragment
+    NumOfTriangles = fragments[0]
+    triangles = []
+    triangleattributes = []
+    triangleneighbors = []
+    for index in range(int(NumOfTriangles)):        
+        #print index
+        line = fd.readline()
+        line.strip() # so we can get the region string
+        fragments = line.split()
+        #print "triangle info", fragments
+        fragments.pop(0) #pop off the index
+        
+        tri = [int(fragments[0]),int(fragments[1]),int(fragments[2])]
+        triangles.append(tri)
+        neighbors = [int(fragments[3]),int(fragments[4]),int(fragments[5])]
+        triangleneighbors.append(neighbors)
+        for x in range(7): # remove index [<vertex #>] [<neigbouring tri #>]
+            line = line[find(line,delimiter):] # remove index
+            line = line.lstrip()
+        stringtag = line.strip()
+        triangleattributes.append(stringtag)
+        
+    ######### loading the segment info
+    line = fd.readline()
+    #print "seg comment line",line
+    fragments = line.split()
+    #for fragment in fragments:
+    #    print fragment
+    NumOfSegments = fragments[0]
+    segments = []
+    segmenttags = []
+    for index in range(int(NumOfSegments)):        
+        #print index
+        line = fd.readline()
+        line.strip() # to get the segment string
+        fragments = line.split()
+        #print fragments
+        fragments.pop(0) #pop off the index
+        seg = [int(fragments[0]),int(fragments[1])]
+        segments.append(seg)
+        line = line[find(line,delimiter):] # remove index
+        line = line.lstrip()
+        line = line[find(line,delimiter):] # remove x
+        line = line.lstrip()
+        line = line[find(line,delimiter):] # remove y
+        stringtag = line.strip()
+        segmenttags.append(stringtag)
+
+            
+    meshDict = {}
+    meshDict['vertices'] = points
+    meshDict['vertex_attributes'] = pointattributes
+    meshDict['triangles'] = triangles
+    meshDict['triangle_tags'] = triangleattributes
+    meshDict['triangle_neighbors'] = triangleneighbors 
+    meshDict['segments'] = segments 
+    meshDict['segment_tags'] = segmenttags
+    meshDict['vertex_attribute_titles'] = vertTitle
+    
+    return meshDict
+    
+def _read_outline(fd):
+    """
+    Note, if a file has no mesh info, it can still be read - the meshdic
+    returned will be 'empty'.
+    """
+    delimiter = " " # warning: split() calls are using default whitespace
+    
+    ######### loading the point info
+    line = fd.readline()
+    #print 'read_outline - line',line
+    fragments = line.split()
+    #for fragment in fragments:
+    if fragments ==[]:
+        NumOfVertices = 0
+        NumOfVertAttributes = 0
+    else:
+        NumOfVertices = fragments[0]
+        NumOfVertAttributes = fragments[1]
+    points = []
+    pointattributes = []
+    for index in range(int(NumOfVertices)):        
+        #print index
+        fragments = fd.readline().split() 
+        #print fragments
+        fragments.pop(0) #pop off the index
+        # pop the x y off so we're left with a list of attributes
+        vert = [float(fragments.pop(0)),float(fragments.pop(0))]
+        points.append(vert)
+        apointattributes  = []
+        #print fragments
+        for fragment in fragments:
+            apointattributes.append(float(fragment))
+        pointattributes.append(apointattributes)
+        
+
+    ######### loading the segment info
+    line = fd.readline()
+    fragments = line.split()
+    #for fragment in fragments:
+    #    print fragment
+    if fragments ==[]:
+        NumOfSegments = 0
+    else:
+        NumOfSegments = fragments[0]
+    segments = []
+    segmenttags = []
+    for index in range(int(NumOfSegments)):  
+        #print index
+        line = fd.readline()
+        fragments = line.split()
+        #print fragments
+        fragments.pop(0) #pop off the index
+        
+        seg = [int(fragments[0]),int(fragments[1])]
+        segments.append(seg)
+        line = line[find(line,delimiter):] # remove index
+        line = line.lstrip()
+        line = line[find(line,delimiter):] # remove x
+        line = line.lstrip()
+        line = line[find(line,delimiter):] # remove y
+        stringtag = line.strip()
+        segmenttags.append(stringtag)  
+
+    ######### loading the hole info
+    line = fd.readline()
+    #print line
+    fragments = line.split()
+    #for fragment in fragments:
+    #    print fragment
+    if fragments ==[]:
+        numOfHoles = 0
+    else:
+        numOfHoles = fragments[0]
+    holes = []
+    for index in range(int(numOfHoles)):        
+        #print index
+        fragments = fd.readline().split()
+        #print fragments
+        fragments.pop(0) #pop off the index
+        hole = [float(fragments[0]),float(fragments[1])]
+        holes.append(hole)
+
+    
+    ######### loading the region info
+    line = fd.readline()
+    #print line
+    fragments = line.split()
+    #for fragment in fragments:
+    #    print fragment
+    if fragments ==[]:
+        numOfRegions = 0
+    else:
+        numOfRegions = fragments[0]
+    regions = []
+    regionattributes = []
+    for index in range(int(numOfRegions)):
+        line = fd.readline()
+        fragments = line.split()
+        #print fragments
+        fragments.pop(0) #pop off the index
+        region = [float(fragments[0]),float(fragments[1])]
+        regions.append(region)
+
+        line = line[find(line,delimiter):] # remove index
+        line = line.lstrip()
+        line = line[find(line,delimiter):] # remove x
+        line = line.lstrip()
+        line = line[find(line,delimiter):] # remove y
+        stringtag = line.strip()
+        regionattributes.append(stringtag)
+    regionmaxareas = []
+    line = fd.readline()
+    for index in range(int(numOfRegions)): # Read in the Max area info
+        line = fd.readline()
+        fragments = line.split()
+        #print fragments
+        # The try is here for format compatibility
+        try:
+            fragments.pop(0) #pop off the index
+            if len(fragments) == 0: #no max area
+                regionmaxareas.append(None)
+            else:
+                regionmaxareas.append(float(fragments[0]))
+        except IndexError, e:
+            regionmaxareas.append(None)
+
+    try:
+        geo_reference = Geo_reference(ASCIIFile=fd)
+    except:
+        #geo_ref not compulsory 
+        geo_reference = None
+        
+    meshDict = {}
+    meshDict['points'] = points
+    meshDict['point_attributes'] = pointattributes
+    meshDict['outline_segments'] = segments 
+    meshDict['outline_segment_tags'] = segmenttags
+    meshDict['holes'] = holes
+    meshDict['regions'] = regions
+    meshDict['region_tags'] = regionattributes
+    meshDict['region_max_areas'] = regionmaxareas
+    meshDict['geo_reference'] = geo_reference
+    
+    #print "meshDict",meshDict 
+    return meshDict
+
+def clean_line(line,delimiter):      
+    """Remove whitespace
+    """
+    #print ">%s" %line
+    line = line.strip()
+    #print "stripped>%s" %line
+    numbers = line.split(delimiter)
+    i = len(numbers) - 1
+    while i >= 0:
+        if numbers[i] == '':
+            numbers.pop(i)
+        else:
+            numbers[i] = numbers[i].strip()
+        
+        i += -1
+    #for num in numbers:
+    #    print "num>%s<" %num
+    return numbers
+
+def _write_ASCII_triangulation(fd,
+                               gen_dict):
+    vertices = gen_dict['vertices']
+    vertices_attributes = gen_dict['vertex_attributes']
+    try:
+        vertices_attribute_titles = gen_dict['vertex_attribute_titles']    
+    except KeyError, e:
+        #FIXME is this the best way? 
+        if vertices_attributes == [] or vertices_attributes[0] == []:
+             vertices_attribute_titles = []
+        else:
+            raise KeyError, e 
+        
+    triangles = gen_dict['triangles']
+    triangles_attributes = gen_dict['triangle_tags']
+    triangle_neighbors = gen_dict['triangle_neighbors']
+        
+    segments = gen_dict['segments']
+    segment_tags = gen_dict['segment_tags']
+     
+    numVert = str(len(vertices))
+    if (numVert == "0") or len(vertices_attributes) == 0:
+        numVertAttrib = "0"
+    else:
+        numVertAttrib = str(len(vertices_attributes[0]))
+    fd.write(numVert + " " + numVertAttrib + " # <# of verts> <# of vert attributes>, next lines <vertex #> <x> <y> [attributes] ...Triangulation Vertices..." + "\n")
+
+    #<vertex #> <x> <y> [attributes]    
+    index = 0 
+    for vert in vertices:
+        attlist = ""
+        for att in vertices_attributes[index]:
+            attlist = attlist + str(att)+" "
+        attlist.strip()
+        fd.write(str(index) + " "
+                 + str(vert[0]) + " "
+                 + str(vert[1]) + " "
+                 + attlist + "\n")
+        index += 1
+
+    # write comments for title 
+    fd.write("# attribute column titles ...Triangulation Vertex Titles..." + "\n")
+    for title in vertices_attribute_titles:
+        fd.write(title + "\n")
+        
+    #<# of triangles>
+    n = len(triangles)
+    fd.write(str(n) + " # <# of triangles>, next lines <triangle #> [<vertex #>] [<neigbouring triangle #>] [attribute of region] ...Triangulation Triangles..." + "\n")
+        
+    # <triangle #> <vertex #>  <vertex #> <vertex #> <neigbouring triangle #> <neigbouring triangle #> <neigbouring triangle #> [attribute of region]
+    for index in range(n):
+        neighbors = ""
+        tri = triangles[index]
+        for neighbor in triangle_neighbors[index]:
+            if neighbor:
+                neighbors += str(neighbor) + " "
+            else:
+                if neighbor == 0:
+                    neighbors +=  "0 "
+                else:
+                    neighbors +=  "-1 "
+        #Warning even though a list is past, only the first value
+        #is written.  There's an assumption that the list only
+        # contains one item. This assumption is made since the
+        #dict that's being passed around is also be used to communicate
+        # with triangle, and it seems to have the option of returning
+        # more than one value for triangle attributex
+        if triangles_attributes[index] == ['']:
+            att = ""
+        else:
+            att = str(triangles_attributes[index])
+        fd.write(str(index) + " "
+                 + str(tri[0]) + " " 
+                 + str(tri[1]) + " " 
+                 + str(tri[2]) + " " 
+                 + neighbors + " "
+                 + att + "\n")
+            
+    #One line:  <# of segments> 
+    fd.write(str(len(segments)) + 
+             " # <# of segments>, next lines <segment #> <vertex #>  <vertex #> [boundary tag] ...Triangulation Segments..." + "\n")
+        
+    #Following lines:  <segment #> <vertex #>  <vertex #> [boundary tag]
+    for i in range(len(segments)):
+        seg = segments[i]
+        fd.write(str(i) + " "
+                 + str(seg[0]) + " " 
+                 + str(seg[1]) + " " 
+                 + str(segment_tags[i]) + "\n")
+
+
+def _write_tsh_file(ofile,mesh_dict):
+            fd = open(ofile,'w')
+            _write_ASCII_triangulation(fd,mesh_dict)
+            _write_ASCII_outline(fd,mesh_dict)    
+            fd.close()
+
+def _write_ASCII_outline(fd,
+                     dict):
+    points = dict['points']
+    point_attributes = dict['point_attributes']
+    segments = dict['outline_segments']
+    segment_tags = dict['outline_segment_tags']
+    holes = dict['holes']
+    regions = dict['regions']
+    region_tags = dict['region_tags']
+    region_max_areas = dict['region_max_areas']
+        
+    num_points = str(len(points))
+    if (num_points == "0"):
+        num_point_atts = "0"
+    else:
+        num_point_atts = str(len(point_attributes[0]))
+    fd.write(num_points + " " + num_point_atts + " # <# of verts> <# of vert attributes>, next lines <vertex #> <x> <y> [attributes] ...Mesh Vertices..." + "\n")
+        
+    # <x> <y> [attributes]
+    for i,point in enumerate(points):
+        attlist = ""
+        for att in point_attributes[i]:
+            attlist = attlist + str(att)+" "
+        attlist.strip()
+        fd.write(str(i) + " "
+                 +str(point[0]) + " "
+                 + str(point[1]) + " "
+                 + attlist + "\n")
+            
+    #One line:  <# of segments> 
+    fd.write(str(len(segments)) + 
+             " # <# of segments>, next lines <segment #> <vertex #>  <vertex #> [boundary tag] ...Mesh Segments..." + "\n")
+        
+    #Following lines:  <vertex #>  <vertex #> [boundary tag]
+    for i,seg in enumerate(segments):
+        fd.write(str(i) + " "
+                 + str(seg[0]) + " " 
+                 + str(seg[1]) + " " 
+                 + str(segment_tags[i]) + "\n")
+
+    #One line:  <# of holes> 
+    fd.write(str(len(holes)) + 
+             " # <# of holes>, next lines <Hole #> <x> <y> ...Mesh Holes..." + "\n")    
+    # <x> <y>
+    for i,h in enumerate(holes):
+        fd.write(str(i) + " "
+                 + str(h[0]) + " "
+                 + str(h[1]) + "\n")
+        
+    #One line:  <# of regions> 
+    fd.write(str(len(regions)) + 
+             " # <# of regions>, next lines <Region #> <x> <y> <tag>...Mesh Regions..." + "\n")    
+    # <index> <x> <y> <tag>
+    #print "regions",regions 
+    for i,r in enumerate(regions):
+        #print "r",r 
+        fd.write(str(i) + " " 
+                 + str(r[0]) + " "
+                 + str(r[1])+ " "
+                 + str(region_tags[i]) + "\n")
+        
+    # <index> [<MaxArea>|""]
+        
+    #One line:  <# of regions> 
+    fd.write(str(len(regions)) + 
+             " # <# of regions>, next lines <Region #> [Max Area] ...Mesh Regions..." + "\n") 
+    for i,r in enumerate(regions):
+        area = str(region_max_areas[i])
+            
+        fd.write(str(i) + " " + area + "\n")
+
+    # geo_reference info
+    if dict.has_key('geo_reference') and not dict['geo_reference'] is None:
+        dict['geo_reference'].write_ASCII(fd)
+
+def _write_msh_file(file_name, mesh):
+    """Write .msh NetCDF file   
+
+    WARNING: This function mangles the mesh data structure
+    """
+  
+    from Scientific.IO.NetCDF import NetCDFFile
+
+    IntType = Int32
+    
+    # 
+    #the triangulation
+    mesh['vertices'] = array(mesh['vertices']).astype(Float)
+    mesh['vertex_attributes'] = array(mesh['vertex_attributes']).astype(Float)
+    mesh['vertex_attribute_titles'] = array(mesh['vertex_attribute_titles']).astype(Character) 
+    mesh['segments'] = array(mesh['segments']).astype(IntType)
+    mesh['segment_tags'] = array(mesh['segment_tags']).astype(Character)
+    mesh['triangles'] = array(mesh['triangles']).astype(IntType)
+    mesh['triangle_tags'] = array(mesh['triangle_tags']) #.astype(Character)
+    mesh['triangle_neighbors'] = array(mesh['triangle_neighbors']).astype(IntType) 
+    
+    #the outline 
+    mesh['points'] = array(mesh['points']).astype(Float)
+    mesh['point_attributes'] = array(mesh['point_attributes']).astype(Float)
+    mesh['outline_segments'] = array(mesh['outline_segments']).astype(IntType)
+    mesh['outline_segment_tags'] = array(mesh['outline_segment_tags']).astype(Character)
+    mesh['holes'] = array(mesh['holes']).astype(Float)
+    mesh['regions'] = array(mesh['regions']).astype(Float)
+    mesh['region_tags'] = array(mesh['region_tags']).astype(Character)
+    mesh['region_max_areas'] = array(mesh['region_max_areas']).astype(Float)
+    
+    #mesh = mesh_dict2array(mesh)
+    #print "new_mesh",new_mesh
+    #print "mesh",mesh 
+    
+    # NetCDF file definition
+    outfile = NetCDFFile(file_name, 'w')
+    
+    #Create new file
+    outfile.institution = 'Geoscience Australia'
+    outfile.description = 'NetCDF format for compact and portable storage ' +\
+                      'of spatial point data'
+    
+    # dimension definitions - fixed
+    outfile.createDimension('num_of_dimensions', 2) #This is 2d data
+    outfile.createDimension('num_of_segment_ends', 2) #Segs have two points
+    outfile.createDimension('num_of_triangle_vertices', 3)
+    outfile.createDimension('num_of_triangle_faces', 3)
+    outfile.createDimension('num_of_region_max_area', 1)
+
+    # Create dimensions, variables and set the variables
+    
+    # trianglulation
+    # vertices
+    if (mesh['vertices'].shape[0] > 0):
+        outfile.createDimension('num_of_vertices', mesh['vertices'].shape[0])
+        outfile.createVariable('vertices', Float, ('num_of_vertices',
+                                                   'num_of_dimensions'))
+        outfile.variables['vertices'][:] = mesh['vertices']
+        if (mesh['vertex_attributes'].shape[0] > 0 and mesh['vertex_attributes'].shape[1] > 0):
+            outfile.createDimension('num_of_vertex_attributes',
+                                    mesh['vertex_attributes'].shape[1])
+            outfile.createDimension('num_of_vertex_attribute_title_chars',
+                                    mesh['vertex_attribute_titles'].shape[1])
+            outfile.createVariable('vertex_attributes',
+                                   Float,
+                                   ('num_of_vertices',
+                                    'num_of_vertex_attributes'))   
+            outfile.createVariable('vertex_attribute_titles',
+                                   Character,
+                                   ( 'num_of_vertex_attributes',
+                                     'num_of_vertex_attribute_title_chars' ))
+            outfile.variables['vertex_attributes'][:] = \
+                                                      mesh['vertex_attributes']
+            outfile.variables['vertex_attribute_titles'][:] = \
+                                     mesh['vertex_attribute_titles']
+    # segments
+    if (mesh['segments'].shape[0] > 0):       
+        outfile.createDimension('num_of_segments',
+                                mesh['segments'].shape[0])
+        outfile.createVariable('segments',
+                               IntType,
+                               ('num_of_segments', 'num_of_segment_ends'))
+        outfile.variables['segments'][:] = mesh['segments']
+        if (mesh['segment_tags'].shape[1] > 0):
+            outfile.createDimension('num_of_segment_tag_chars',
+                                    mesh['segment_tags'].shape[1])
+            outfile.createVariable('segment_tags',
+                                   Character,
+                                   ('num_of_segments',
+                                    'num_of_segment_tag_chars'))
+            outfile.variables['segment_tags'][:] = mesh['segment_tags']
+    # triangles    
+    if (mesh['triangles'].shape[0] > 0):
+        outfile.createDimension('num_of_triangles',
+                                mesh['triangles'].shape[0])
+        outfile.createVariable('triangles',
+                               IntType,
+                               ('num_of_triangles',
+                                'num_of_triangle_vertices'))
+        outfile.createVariable('triangle_neighbors',
+                               IntType,
+                               ('num_of_triangles',
+                                'num_of_triangle_faces'))
+        outfile.variables['triangles'][:] = mesh['triangles']
+        outfile.variables['triangle_neighbors'][:] = mesh['triangle_neighbors']
+        if (mesh['triangle_tags'].shape[1] > 0):
+            outfile.createDimension('num_of_triangle_tag_chars',
+                                    mesh['triangle_tags'].shape[1]) 
+            outfile.createVariable('triangle_tags',
+                                   Character,
+                                   ('num_of_triangles',
+                                    'num_of_triangle_tag_chars'))
+            outfile.variables['triangle_tags'][:] = mesh['triangle_tags']
+    
+
+    # outline
+    # points
+    if (mesh['points'].shape[0] > 0):
+        outfile.createDimension('num_of_points', mesh['points'].shape[0])
+        outfile.createVariable('points', Float, ('num_of_points',
+                                                 'num_of_dimensions'))
+        outfile.variables['points'][:] = mesh['points'] 
+        if (mesh['point_attributes'].shape[0] > 0  and mesh['point_attributes'].shape[1] > 0):
+            outfile.createDimension('num_of_point_attributes',
+                                    mesh['point_attributes'].shape[1])
+            outfile.createVariable('point_attributes',
+                                   Float,
+                                   ('num_of_points',
+                                    'num_of_point_attributes'))
+            outfile.variables['point_attributes'][:] = mesh['point_attributes']
+    # outline_segments 
+    if (mesh['outline_segments'].shape[0] > 0):
+        outfile.createDimension('num_of_outline_segments',
+                                mesh['outline_segments'].shape[0])
+        outfile.createVariable('outline_segments',
+                               IntType,
+                               ('num_of_outline_segments',
+                                'num_of_segment_ends'))
+        outfile.variables['outline_segments'][:] = mesh['outline_segments']
+        if (mesh['outline_segment_tags'].shape[1] > 0):
+            outfile.createDimension('num_of_outline_segment_tag_chars',
+                                    mesh['outline_segment_tags'].shape[1])
+            outfile.createVariable('outline_segment_tags',
+                                   Character,
+                                   ('num_of_outline_segments',
+                                    'num_of_outline_segment_tag_chars'))
+            outfile.variables['outline_segment_tags'][:] = mesh['outline_segment_tags']
+    # holes
+    if (mesh['holes'].shape[0] > 0):
+        outfile.createDimension('num_of_holes', mesh['holes'].shape[0])
+        outfile.createVariable('holes', Float, ('num_of_holes',
+                                                'num_of_dimensions'))
+        outfile.variables['holes'][:] = mesh['holes']
+    # regions
+    if (mesh['regions'].shape[0] > 0):
+        outfile.createDimension('num_of_regions', mesh['regions'].shape[0])
+        outfile.createVariable('regions', Float, ('num_of_regions',
+                                                  'num_of_dimensions'))
+        outfile.createVariable('region_max_areas',
+                               Float,
+                               ('num_of_regions',))
+        outfile.variables['regions'][:] = mesh['regions']
+        outfile.variables['region_max_areas'][:] = mesh['region_max_areas']
+        if (mesh['region_tags'].shape[1] > 0):
+            outfile.createDimension('num_of_region_tag_chars',
+                                    mesh['region_tags'].shape[1])
+            outfile.createVariable('region_tags',
+                                   Character,
+                                   ('num_of_regions',
+                                    'num_of_region_tag_chars'))
+            outfile.variables['region_tags'][:] = mesh['region_tags']
+
+    # geo_reference info
+    if mesh.has_key('geo_reference') and not mesh['geo_reference'] == None:
+        mesh['geo_reference'].write_NetCDF(outfile)
+                                                 
+    outfile.close()
+
+
+    
+def _read_msh_file(file_name):
+    """
+    Read in an msh file.
+
+    """
+        
+    from Scientific.IO.NetCDF import NetCDFFile        	
+           
+    #Check contents
+    #Get NetCDF
+    
+    # see if the file is there.  Throw a QUIET IO error if it isn't
+    fd = open(file_name,'r')
+    fd.close()
+
+    #throws prints to screen if file not present
+    fid = NetCDFFile(file_name, 'r') 
+
+    mesh = {}
+    # Get the variables
+    # the triangulation
+    try:
+        mesh['vertices'] = fid.variables['vertices'][:]
+    except KeyError:
+        mesh['vertices'] = array([])
+    try:
+        mesh['vertex_attributes'] = fid.variables['vertex_attributes'][:]
+    except KeyError:
+        mesh['vertex_attributes'] = []
+        for ob in mesh['vertices']:
+            mesh['vertex_attributes'].append([])
+    mesh['vertex_attribute_titles'] = []
+    try:
+        titles = fid.variables['vertex_attribute_titles'][:]
+        for i, title in enumerate(titles):
+            mesh['vertex_attribute_titles'].append(titles[i].tostring().strip())
+    except KeyError:
+        pass  
+    try:
+        mesh['segments'] = fid.variables['segments'][:]
+    except KeyError:
+        mesh['segments'] = array([])
+    mesh['segment_tags'] =[]
+    try:
+        tags = fid.variables['segment_tags'][:]
+        for i, tag in enumerate(tags):
+            mesh['segment_tags'].append(tags[i].tostring().strip())
+    except KeyError:
+        for ob in mesh['segments']:
+            mesh['segment_tags'].append('')
+    try:
+        mesh['triangles'] = fid.variables['triangles'][:]
+        mesh['triangle_neighbors'] = fid.variables['triangle_neighbors'][:]
+    except KeyError:
+        mesh['triangles'] = array([])
+        mesh['triangle_neighbors'] = array([])
+    mesh['triangle_tags'] =[]
+    try:
+        tags = fid.variables['triangle_tags'][:]
+        for i, tag in enumerate(tags):
+            mesh['triangle_tags'].append(tags[i].tostring().strip())
+    except KeyError:
+        for ob in mesh['triangles']:
+            mesh['triangle_tags'].append('')
+    
+    #the outline 
+    try:
+        mesh['points'] = fid.variables['points'][:]
+    except KeyError:
+        mesh['points'] = []
+    try:
+        mesh['point_attributes'] = fid.variables['point_attributes'][:]
+    except KeyError:
+        mesh['point_attributes'] = []
+        for point in mesh['points']:
+            mesh['point_attributes'].append([])
+    try:
+        mesh['outline_segments'] = fid.variables['outline_segments'][:]
+    except KeyError:
+        mesh['outline_segments'] = array([])
+    mesh['outline_segment_tags'] =[]
+    try:
+        tags = fid.variables['outline_segment_tags'][:]
+        for i, tag in enumerate(tags):
+            mesh['outline_segment_tags'].append(tags[i].tostring().strip())
+    except KeyError:
+        for ob in mesh['outline_segments']:
+            mesh['outline_segment_tags'].append('')
+    try:
+        mesh['holes'] = fid.variables['holes'][:]
+    except KeyError:
+        mesh['holes'] = array([])
+    try:
+        mesh['regions'] = fid.variables['regions'][:]
+    except KeyError:
+        mesh['regions'] = array([])
+    mesh['region_tags'] =[]
+    try:
+        tags = fid.variables['region_tags'][:]
+        for i, tag in enumerate(tags):
+            mesh['region_tags'].append(tags[i].tostring().strip())
+    except KeyError:
+        for ob in mesh['regions']:
+            mesh['region_tags'].append('')
+    try:
+        mesh['region_max_areas'] = fid.variables['region_max_areas'][:]
+    except KeyError:
+        mesh['region_max_areas'] = array([])
+    #mesh[''] = fid.variables[''][:]
+      
+    try:
+        geo_reference = Geo_reference(NetCDFObject=fid)
+        mesh['geo_reference'] = geo_reference
+    except AttributeError, e:
+        #geo_ref not compulsory 
+        mesh['geo_reference'] = None
+    
+    fid.close()
+      
+    return mesh
+           
+
+## used by alpha shapes
+    
+def export_boundary_file( file_name, points, title, delimiter = ','):
+    """
+    export a file, ofile, with the format
+    
+    First line: Title variable
+    Following lines:  [point index][delimiter][point index] 
+
+    file_name - the name of the new file
+    points - List of point index pairs [[p1, p2],[p3, p4]..] 
+    title - info to write in the first line
+    """
+    
+    fd = open(file_name,'w')
+    
+    fd.write(title+"\n")
+    #[point index][delimiter][point index] 
+    for point in points:
+        fd.write( str(point[0]) + delimiter
+                  + str(point[1]) + "\n")
+    fd.close()
+
+
+###
+#  IMPORT/EXPORT XYA FILES
+###
+
+def export_points_file(ofile,point_dict):
+    """
+    write a points file, ofile, as a binary (.msh) or text (.tsh) file
+    """
+    #this was done for all keys in the mesh file.
+    #if not mesh_dict.has_key('points'):
+    #    mesh_dict['points'] = []
+    if (ofile[-4:] == ".xya"):
+        _write_xya_file(ofile, point_dict)
+    elif (ofile[-4:] == ".pts"):
+        _write_pts_file(ofile, point_dict)
+    else:
+        msg = 'Unknown file type %s ' %ofile
+        raise IOError, msg 
+               
+def import_points_file(ofile, delimiter = None, verbose = False):
+    """
+    load an .xya or .pts file
+
+    Note: will throw an IOError if it can't load the file.
+    Catch these!
+    """
+    
+    if ofile[-4:]== ".xya":
+        try:
+            if delimiter == None:
+                try:
+                    fd = open(ofile)
+                    points_dict = _read_xya_file(fd, ',')
+                except SyntaxError:
+                    fd.close()
+                    fd = open(ofile)
+                    points_dict = _read_xya_file(fd, ' ')
+            else:
+                fd = open(ofile)
+                points_dict = _read_xya_file(fd, delimiter)
+            fd.close()
+        except (IndexError,ValueError,SyntaxError):
+            fd.close()    
+            msg = 'Could not open file %s ' %ofile
+            raise IOError, msg
+        except IOError:
+            # Catch this to add an error message
+            msg = 'Could not open file %s ' %ofile
+            raise IOError, msg
+        
+    elif ofile[-4:]== ".pts":
+        try:
+            points_dict = _read_pts_file(ofile, verbose)        
+        except IOError, e:    
+            msg = 'Could not open file %s ' %ofile
+            raise IOError, msg
+    else:      
+        msg = 'Extension %s is unknown' %ofile[-4:]
+        raise IOError, msg
+    return points_dict
+
+def extent_point_atts(point_atts):
+    """
+    Returns 4 points representing the extent
+    This losses attribute info.
+    """
+    point_atts['pointlist'] = extent(point_atts['pointlist'])
+    point_atts['attributelist'] = {}
+    return point_atts
+    
+def extent(points):
+    points = array(points).astype(Float)
+    max_x = min_x = points[0][0]
+    max_y = min_y = points[0][1]
+    for point in points[1:]:
+        x = point[0] 
+        if x > max_x: max_x = x
+        if x < min_x: min_x = x           
+        y = point[1] 
+        if y > max_y: max_y = y
+        if y < min_y: min_y = y
+    extent = array([[min_x,min_y],[max_x,min_y],[max_x,max_y],[min_x,max_y]])
+    #print "extent",extent 
+    return extent
+    
+def reduce_pts(infile, outfile, max_points, verbose = False):
+    point_atts = _read_pts_file(infile)
+    while point_atts['pointlist'].shape[0] > max_points:
+        if verbose: print "point_atts['pointlist'].shape[0]"
+        point_atts = half_pts(point_atts)
+    export_points_file(outfile, point_atts)
+ 
+def produce_half_point_files(infile, max_points, delimiter, verbose = False):
+    point_atts = _read_pts_file(infile)
+    root, ext = splitext(infile)
+    outfiles = []
+    if verbose: print "# of points", point_atts['pointlist'].shape[0]
+    while point_atts['pointlist'].shape[0] > max_points:
+        point_atts = half_pts(point_atts)
+        if verbose: print "# of points", point_atts['pointlist'].shape[0]
+        outfile = root + delimiter + str(point_atts['pointlist'].shape[0]) + ext
+        outfiles.append(outfile)
+        export_points_file(outfile,
+                  point_atts)
+    return outfiles
+    
+def point_atts2array(point_atts):
+    point_atts['pointlist'] = array(point_atts['pointlist']).astype(Float)
+    
+    for key in point_atts['attributelist'].keys():
+        point_atts['attributelist'][key]= array(point_atts['attributelist'][key]).astype(Float)
+    return point_atts
+    
+def half_pts(point_atts):
+    point_atts2array(point_atts)
+    point_atts['pointlist'] = point_atts['pointlist'][::2]
+    
+    for key in point_atts['attributelist'].keys():
+        point_atts['attributelist'][key]=  point_atts['attributelist'][key] [::2]
+    return point_atts
+
+def concatinate_attributelist(dic):
+    """
+    giving a dic[attribute title] = attribute
+    return list of attribute titles, array of attributes 
+    """
+    point_attributes = array([]).astype(Float)
+    keys = dic.keys()
+    key = keys.pop(0)
+    point_attributes = reshape(dic[key],(dic[key].shape[0],1))
+    for key in keys:
+        #point_attributes = concatenate([point_attributes, dic[key]], axis=1)
+        reshaped = reshape(dic[key],(dic[key].shape[0],1))
+        point_attributes = concatenate([point_attributes, reshaped], axis=1)
+    return dic.keys(), point_attributes
+
+def _read_pts_file(file_name, verbose = False):
+    """Read .pts NetCDF file
+   
+    Return a dic of array of points, and dic of array of attribute
+
+    eg
+    dic['points'] = [[1.0,2.0],[3.0,5.0]]
+    dic['attributelist']['elevation'] = [[7.0,5.0]
+    """
+    #FIXME: (DSG)This format has issues.
+    # There can't be an attribute called points 
+    # consider format change
+
+    from Scientific.IO.NetCDF import NetCDFFile
+
+    if verbose: print 'Reading ', file_name
+
+    
+    # see if the file is there.  Throw a QUIET IO error if it isn't
+    fd = open(file_name,'r')
+    fd.close()
+
+    #throws prints to screen if file not present
+    fid = NetCDFFile(file_name, 'r') 
+
+    point_atts = {}  
+    # Get the variables
+    point_atts['pointlist'] = array(fid.variables['points'])
+    keys = fid.variables.keys()
+    if verbose: print 'Got %d variables: %s' %(len(keys), keys)
+    try:
+        keys.remove('points')
+    except IOError, e:       
+        fid.close()    
+        msg = 'Expected keyword "points" but could not find it'
+        raise IOError, msg
+
+    attributes = {}
+    for key in keys:
+        if verbose: print "reading attribute '%s'" %key
+        
+        attributes[key] = array(fid.variables[key])
+        
+    point_atts['attributelist'] = attributes
+    
+    try:
+        geo_reference = Geo_reference(NetCDFObject=fid)
+        point_atts['geo_reference'] = geo_reference
+    except AttributeError, e:
+        #geo_ref not compulsory 
+        point_atts['geo_reference'] = None
+    
+    
+    fid.close()
+    
+    #print "point_atts",point_atts 
+    return point_atts
+
+def _write_pts_file(file_name, point_atts):
+    """Write .pts NetCDF file   
+
+    WARNING: This function mangles the point_atts data structure
+    """
+    #FIXME: (DSG)This format has issues.
+    # There can't be an attribute called points 
+    # consider format change
+    
+    from Scientific.IO.NetCDF import NetCDFFile
+    point_atts2array(point_atts)
+    # NetCDF file definition
+    outfile = NetCDFFile(file_name, 'w')
+    
+    #Create new file
+    outfile.institution = 'Geoscience Australia'
+    outfile.description = 'NetCDF format for compact and portable storage ' +\
+                      'of spatial point data'
+    
+    # dimension definitions
+    shape = point_atts['pointlist'].shape[0]
+    outfile.createDimension('number_of_points', shape)  
+    outfile.createDimension('number_of_dimensions', 2) #This is 2d data
+    
+    # variable definition
+    outfile.createVariable('points', Float, ('number_of_points',
+                                             'number_of_dimensions'))
+
+    #create variables  
+    outfile.variables['points'][:] = point_atts['pointlist'] #.astype(Float32)
+    for key in point_atts['attributelist'].keys():
+        outfile.createVariable(key, Float, ('number_of_points',))
+        outfile.variables[key][:] = point_atts['attributelist'][key] #.astype(Float32)
+        
+    if point_atts.has_key('geo_reference') and not point_atts['geo_reference'] == None:
+        point_atts['geo_reference'].write_NetCDF(outfile)
+        
+    outfile.close() 
+   
+def _read_xya_file(fd, delimiter):
+    #lines = fd.readlines()
+    points = []
+    pointattributes = []
+    #if len(lines) <= 1:
+    #    raise SyntaxError
+    title = fd.readline()
+    #title = lines.pop(0) # the first (title) line
+    att_names = clean_line(title,delimiter)
+
+    att_dict = {}
+    line = fd.readline()
+    numbers = clean_line(line,delimiter)
+    #for line in lines:
+    while len(numbers) > 1:
+        #print "line >%s" %line
+        #numbers = clean_line(line,delimiter)
+        #print "numbers >%s<" %numbers
+        #if len(numbers) < 2 and numbers != []:
+            
+            # A line without two numbers
+            # or a bad delimiter
+            #FIXME dsg-dsg change error that is raised.
+            #raise SyntaxError
+        if numbers != []:
+            try:
+                x = float(numbers[0])
+                y = float(numbers[1])
+                points.append([x,y])
+                numbers.pop(0)
+                numbers.pop(0)
+                #attributes = []
+                #print "att_names",att_names
+                #print "numbers",numbers 
+                if len(att_names) != len(numbers):
+                    fd.close()
+                    # It might not be a problem with the title
+                    #raise TitleAmountError
+                    raise IOError
+                for i,num in enumerate(numbers):
+                    num.strip()
+                    if num != '\n' and num != '':
+                        #attributes.append(float(num))
+                        att_dict.setdefault(att_names[i],[]).append(float(num))
+                    
+            except ValueError:
+                raise SyntaxError
+        line = fd.readline()
+        numbers = clean_line(line,delimiter)
+        
+    if line == '':
+        # end of file
+        geo_reference = None
+    else:
+        geo_reference = Geo_reference(ASCIIFile=fd,read_title=line)
+    
+    xya_dict = {}
+    xya_dict['pointlist'] = array(points).astype(Float)
+    
+    for key in att_dict.keys():
+        att_dict[key] = array(att_dict[key]).astype(Float)
+    xya_dict['attributelist'] = att_dict
+    xya_dict['geo_reference'] = geo_reference
+    #print "xya_dict",xya_dict 
+    return xya_dict
+
+#FIXME(dsg), turn this dict plus methods into a class?
+def take_points(dict,indices_to_keep):
+    dict = point_atts2array(dict)
+    #FIXME maybe the points data structure should become a class?
+    dict['pointlist'] = take(dict['pointlist'],indices_to_keep)
+
+    for key in dict['attributelist'].keys():
+        dict['attributelist'][key]= take(dict['attributelist'][key],
+                                         indices_to_keep)
+    return dict
+    
+def add_point_dictionaries (dict1, dict2):
+    """
+    """
+    dict1 = point_atts2array(dict1)
+    dict2 = point_atts2array(dict2)
+    
+    combined = {}  
+    combined['pointlist'] = concatenate((dict2['pointlist'],
+                                         dict1['pointlist']),axis=0)   
+    atts = {}   
+    for key in dict2['attributelist'].keys():
+        atts[key]= concatenate((dict2['attributelist'][key],
+                                dict1['attributelist'][key]), axis=0)
+    combined['attributelist']=atts
+    combined['geo_reference'] = dict1['geo_reference']
+    return combined
+                 
+def _write_xya_file( file_name, xya_dict, delimiter = ','):
+    """
+    export a file, ofile, with the xya format
+    
+    """
+    points = xya_dict['pointlist'] 
+    pointattributes = xya_dict['attributelist']
+    
+    fd = open(file_name,'w')
+ 
+    titlelist = ""
+    for title in pointattributes.keys():
+        titlelist = titlelist + title + delimiter
+    titlelist = titlelist[0:-len(delimiter)] # remove the last delimiter
+    fd.write(titlelist+"\n")
+    #<vertex #> <x> <y> [attributes]
+    for i,vert in enumerate( points):
+        
+        attlist = ","
+        for att in pointattributes.keys():
+            attlist = attlist + str(pointattributes[att][i])+ delimiter
+        attlist = attlist[0:-len(delimiter)] # remove the last delimiter
+        attlist.strip()
+        fd.write( str(vert[0]) + delimiter
+                  + str(vert[1])
+                  + attlist + "\n")
+    
+    # geo_reference info
+    if xya_dict.has_key('geo_reference') and \
+           not xya_dict['geo_reference'] is None:
+        xya_dict['geo_reference'].write_ASCII(fd)
+    fd.close()
+     
+if __name__ == "__main__":
+    pass
Index: /inundation/pmesh/load_mesh/test_loadASCII.py
===================================================================
--- /inundation/pmesh/load_mesh/test_loadASCII.py	(revision 1719)
+++ /inundation/pmesh/load_mesh/test_loadASCII.py	(revision 1719)
@@ -0,0 +1,1054 @@
+#!/usr/bin/env python
+#
+
+import tempfile
+import unittest
+
+import os
+import tempfile
+
+from os.path import splitext
+
+from Numeric import array, allclose 
+    
+from loadASCII import *
+from coordinate_transforms.geo_reference import Geo_reference
+import loadASCII
+
+class loadASCIITestCase(unittest.TestCase):
+    def setUp(self):
+        self.dict ={}
+        self.dict['outline_segments'] = [(0, 1), (1, 2), (0, 2), (0, 3)]
+        self.dict['outline_segment_tags'] = ['50', '40', '30', '20']
+        self.dict['holes'] = [(0.2, 0.6)]
+        self.dict['point_attributes'] = [[5, 2], [4, 2], [3, 2], [2,2]]
+        self.dict['regions'] = [(0.3, 0.3),(0.3, 0.4)]
+        self.dict['region_tags'] = ['1.3', 'yeah']
+        self.dict['region_max_areas'] = [36.0,-7.1]
+        self.dict['points'] = [(0.0, 0.0), (0.0, 4.0), (4.0, 0.0), (1.0, 1.0)]
+        self.dict['vertices'] = [(0.0, 0.0), (0.0, 4.0),
+                                 (4.0, 0.0), (1.0, 1.0), (2.0, 2.0)]
+        self.dict['triangles'] = [(3, 2, 4), (1, 0, 3),
+                                             (3, 4,1), (2, 3, 0)]
+        self.dict['segments'] = [(0, 1), (1, 4), (2, 0),
+                                            (0, 3), (4, 2)]
+        self.dict['triangle_tags'] = ['1.3', '1.3',
+                                      '1.3', '1.3']
+        self.dict['vertex_attributes'] = [[1.2,2.], [1.2,2.],
+                                          [1.2,2.], [1.2,2.], [1.2,3.]]
+        self.dict['triangle_neighbors'] = [[-1, 2, 3], [3, 2, -1],
+                                           [-1, 1, 0], [1, -1, 0]]
+        self.dict['segment_tags'] = ['50', '40', '30', '20', '40']
+        self.dict['vertex_attribute_titles'] = ['bed elevation', 'height']
+        self.dict['geo_reference'] = Geo_reference(56,1.9,1.9)
+        
+        self.sparse_dict ={}
+        self.sparse_dict['outline_segments'] = []
+        self.sparse_dict['outline_segment_tags'] = []
+        self.sparse_dict['holes'] = []
+        self.sparse_dict['points'] = [(0.0, 0.0),(9,8)]
+        self.sparse_dict['point_attributes'] = [[],[]] # points don't have to have
+                                                    # attributes
+        self.sparse_dict['regions'] = []
+        self.sparse_dict['region_tags'] = []
+        self.sparse_dict['region_max_areas'] = []
+
+        self.sparse_dict['vertices'] = []
+        self.sparse_dict['triangles'] = []
+        self.sparse_dict['segments'] = []
+        self.sparse_dict['triangle_tags'] = []
+        self.sparse_dict['vertex_attributes'] = []
+        self.sparse_dict['triangle_neighbors'] = []
+        self.sparse_dict['segment_tags'] = []
+        self.sparse_dict['vertex_attribute_titles'] = []
+        
+        self.blank_dict ={}
+        self.blank_dict['outline_segments'] = []
+        self.blank_dict['outline_segment_tags'] = []
+        self.blank_dict['holes'] = []
+        self.blank_dict['points'] = []
+        self.blank_dict['point_attributes'] = [] 
+        self.blank_dict['regions'] = []
+        self.blank_dict['region_tags'] = []
+        self.blank_dict['region_max_areas'] = []
+        self.blank_dict['vertices'] = []
+        self.blank_dict['triangles'] = []
+        self.blank_dict['segments'] = []
+        self.blank_dict['triangle_tags'] = []
+        self.blank_dict['vertex_attributes'] = []
+        self.blank_dict['triangle_neighbors'] = []
+        self.blank_dict['segment_tags'] = []
+        self.blank_dict['vertex_attribute_titles'] = []
+        
+        self.tri_dict ={}
+        self.tri_dict['outline_segments'] = [[0,1]]
+        self.tri_dict['outline_segment_tags'] = ['']
+        self.tri_dict['holes'] = []
+        self.tri_dict['points'] = [(9,8),(7,8)]
+        self.tri_dict['point_attributes'] = [[],[]] 
+        self.tri_dict['regions'] = []
+        self.tri_dict['region_tags'] = []
+        self.tri_dict['region_max_areas'] = []
+        self.tri_dict['vertices'] = [[9,8],[7,8], [4,5]]
+        self.tri_dict['triangles'] = [[0,1,2]]
+        self.tri_dict['segments'] = [[0,1]]
+        self.tri_dict['triangle_tags'] = ['']
+        self.tri_dict['vertex_attributes'] = [[],[],[]]
+        self.tri_dict['triangle_neighbors'] = [[0,0,0]]
+        self.tri_dict['segment_tags'] = ['']
+        self.tri_dict['vertex_attribute_titles'] = []
+        
+        self.seg_dict ={}
+        self.seg_dict['outline_segments'] = [[0,1]]
+        self.seg_dict['outline_segment_tags'] = ['']
+        self.seg_dict['holes'] = []
+        self.seg_dict['points'] = [(9,8),(7,8)]
+        self.seg_dict['point_attributes'] = [[],[]]  
+        self.seg_dict['regions'] = [(5,4)]
+        self.seg_dict['region_tags'] = [''] 
+        self.seg_dict['region_max_areas'] = [-999]
+        self.seg_dict['vertices'] = [(9,8),(7,8)]
+        self.seg_dict['triangles'] = []
+        self.seg_dict['segments'] = [[0,1]]
+        self.seg_dict['triangle_tags'] = []
+        self.seg_dict['vertex_attributes'] = [[],[]]
+        self.seg_dict['triangle_neighbors'] = []
+        self.seg_dict['segment_tags'] = ['']
+        self.seg_dict['vertex_attribute_titles'] = []
+        
+        self.reg_dict ={}
+        self.reg_dict['outline_segments'] = [[0,1]]
+        self.reg_dict['outline_segment_tags'] = ['']
+        self.reg_dict['holes'] = []
+        self.reg_dict['points'] = [(9,8),(7,8)]
+        self.reg_dict['point_attributes'] = [[],[]] 
+        self.reg_dict['regions'] = [(5,4)]
+        self.reg_dict['region_tags'] = ['']
+        self.reg_dict['region_max_areas'] = []
+        self.reg_dict['vertices'] = [(9,8),(7,8)]
+        self.reg_dict['triangles'] = []
+        self.reg_dict['segments'] = [[0,1]]
+        self.reg_dict['triangle_tags'] = []
+        self.reg_dict['vertex_attributes'] = [[],[]]
+        self.reg_dict['triangle_neighbors'] = []
+        self.reg_dict['segment_tags'] = ['']
+        self.reg_dict['vertex_attribute_titles'] = []
+        
+        self.triangle_tags_dict ={}
+        self.triangle_tags_dict['outline_segments'] = [(0, 1), (1, 2), (0, 2), (0, 3)]
+        self.triangle_tags_dict['outline_segment_tags'] = ['50', '40', '30', '20']
+        self.triangle_tags_dict['holes'] = [(0.2, 0.6)]
+        self.triangle_tags_dict['point_attributes'] = [[5, 2], [4, 2], [3, 2], [2,2]]
+        self.triangle_tags_dict['regions'] = [(0.3, 0.3),(0.3, 0.4)]
+        self.triangle_tags_dict['region_tags'] = ['1.3', 'yeah']
+        self.triangle_tags_dict['region_max_areas'] = [36.0,-7.1]
+        self.triangle_tags_dict['points'] = [(0.0, 0.0), (0.0, 4.0), (4.0, 0.0), (1.0, 1.0)]
+        self.triangle_tags_dict['vertices'] = [(0.0, 0.0), (0.0, 4.0),
+                                 (4.0, 0.0), (1.0, 1.0), (2.0, 2.0)]
+        self.triangle_tags_dict['triangles'] = [(3, 2, 4), (1, 0, 3),
+                                             (3, 4,1), (2, 3, 0)]
+        self.triangle_tags_dict['segments'] = [(0, 1), (1, 4), (2, 0),
+                                            (0, 3), (4, 2)]
+        self.triangle_tags_dict['triangle_tags'] = ['yeah', '1.3',
+                                      '1.3', '']
+        self.triangle_tags_dict['vertex_attributes'] = [[1.2,2.], [1.2,2.],
+                                          [1.2,2.], [1.2,2.], [1.2,3.]]
+        self.triangle_tags_dict['triangle_neighbors'] = [[-1, 2, 3], [3, 2, -1],
+                                           [-1, 1, 0], [1, -1, 0]]
+        self.triangle_tags_dict['segment_tags'] = ['50', '40', '30', '20', '40']
+        self.triangle_tags_dict['vertex_attribute_titles'] = ['bed elevation', 'height']
+        self.triangle_tags_dict['geo_reference'] = Geo_reference(56,1.9,1.9)
+        
+    def tearDown(self):
+        pass
+
+  ############### .TSH ##########      
+    def test_export_mesh_file(self):
+        import os
+        import tempfile
+        
+        meshDict = self.dict
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName, meshDict)
+        loadedDict = import_mesh_file(fileName)
+        
+        #print "*(*( meshDict"
+        #print meshDict
+        #print "*(*(  loadedDcit"
+        #print loadedDict
+        #print "*(*("
+        
+        self.failUnless(array(meshDict['vertices'])  ==
+                        array(loadedDict['vertices']),
+                         'test_export_mesh_file failed. Test 1')
+        self.failUnless(array(meshDict['triangles'])  ==
+                        array(loadedDict['triangles']),
+                         'test_export_mesh_file failed. Test 2')
+        self.failUnless(array(meshDict['segments'])  ==
+                        array(loadedDict['segments']),
+                         'test_export_mesh_file failed. Test 3')
+        self.failUnless(array(meshDict['triangle_tags'])  ==
+                        array(loadedDict['triangle_tags']),
+                         'test_export_mesh_file failed. Test 4')
+        
+        self.failUnless(meshDict['vertex_attributes']  ==
+                        loadedDict['vertex_attributes'],
+                         'test_export_mesh_file failed. Test 5')
+        self.failUnless(array(meshDict['triangle_neighbors'])  ==
+                        array(loadedDict['triangle_neighbors']),
+                         'test_export_mesh_file failed. Test 6')
+        self.failUnless(array(meshDict['segment_tags'])  ==
+                        array(loadedDict['segment_tags']),
+                         'test_export_mesh_file failed. Test 7')
+        self.failUnless(array(meshDict['vertex_attribute_titles'])  ==
+                        array(loadedDict['vertex_attribute_titles']),
+                         'test_export_mesh_file failed. Test 8')
+        self.failUnless(array(meshDict['geo_reference'])  ==
+                        array(loadedDict['geo_reference']),
+                         'test_export_mesh_file failed. Test 9')
+            
+        os.remove(fileName)
+ 
+    def test_read_write_tsh_file(self):
+        dict = self.dict.copy()
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = import_mesh_file(fileName)
+        os.remove(fileName)
+        dict = self.dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************"       
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_file')
+        
+    def test_read_write_tsh_fileII(self):
+        dict = self.sparse_dict.copy()
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = import_mesh_file(fileName)
+        dict = self.sparse_dict   
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_file')
+        os.remove(fileName)
+        
+    def test_read_write_tsh_fileIII(self):
+        dict = self.blank_dict.copy()
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = import_mesh_file(fileName)
+        os.remove(fileName)
+        dict = self.blank_dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************"       
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_fileIII')
+
+    def test_read_write_tsh_file4(self):
+        dict = self.seg_dict.copy()
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = import_mesh_file(fileName)
+        os.remove(fileName)
+        dict = self.seg_dict   
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_file4')
+
+    def test_read_write_tsh_file5(self):
+        dict = self.triangle_tags_dict.copy()
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = import_mesh_file(fileName)
+        dict = self.triangle_tags_dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************"       
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_file5')  
+        os.remove(fileName)
+
+    def test_read_write_tsh_file6(self):
+        dict = self.tri_dict.copy()
+        fileName = tempfile.mktemp(".tsh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = import_mesh_file(fileName)
+        dict = self.tri_dict    
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_file6')  
+        os.remove(fileName)
+        
+########################## BAD .TSH ##########################
+
+    def test_load_bad_no_file_tsh(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".tsh")
+        #print fileName
+        try:
+            dict = import_mesh_file(fileName)
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'imaginary file did not raise error!')
+         
+    def test_read_write_tsh_file_bad(self):
+        dict = self.tri_dict.copy()
+        fileName = tempfile.mktemp(".xxx")
+        try:
+            export_mesh_file(fileName,dict)
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad tsh file did not raise error!')       
+        
+    def test_import_tsh_bad(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".tsh")
+        file = open(fileName,"w")
+        #   this is  a bad tsh file
+        file.write("elevn\n\
+1.0 what \n\
+0.0 the \n\
+1.0 !!! \n")
+        file.close()
+        #print fileName
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad tsh file did not raise error!')       
+        os.remove(fileName)
+
+    def test_import_tsh3(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".tsh")
+        file = open(fileName,"w")
+        file.write("1.0 \n\
+showme1.0 0.0 10.0 \n\
+0.0 1.0\n\
+13.0 \n")
+        file.close()
+        #print fileName
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad tsh file did not raise error!')
+        
+        os.remove(fileName)         
+
+            
+  ############### .MSH ##########
+        
+    def test_read_write_msh_file(self):
+        dict = self.dict.copy()
+        fileName = tempfile.mktemp(".msh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = loadASCII._read_msh_file(fileName)
+        os.remove(fileName)
+        dict = self.dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************"
+        self.check_mesh_dicts(loaded_dict,dict,'test_read_write_msh_file') 
+
+    def test_read_write_msh_fileII(self):
+        dict = self.sparse_dict.copy()
+        fileName = tempfile.mktemp(".msh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = loadASCII._read_msh_file(fileName)
+        os.remove(fileName)
+        dict = self.sparse_dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************"       
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_fileII')
+              
+    def test_read_write_msh_fileIII(self):
+        dict = self.blank_dict.copy()
+        fileName = tempfile.mktemp(".msh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = loadASCII._read_msh_file(fileName)
+        os.remove(fileName)
+        dict = self.blank_dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************"       
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_fileIII')
+       
+    def test_read_write_msh_file4(self):
+        dict = self.seg_dict.copy()
+        fileName = tempfile.mktemp(".msh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = loadASCII._read_msh_file(fileName)
+        os.remove(fileName)
+        dict = self.seg_dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************" 
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_fileIII')
+        
+    def test_read_write_msh_file5(self):
+        dict = self.triangle_tags_dict.copy()
+        fileName = tempfile.mktemp(".msh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = loadASCII._read_msh_file(fileName)
+        os.remove(fileName)
+        dict = self.triangle_tags_dict
+        #print "msh_file5*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************" 
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_fileIII')
+                                       
+        
+    def test_read_write_msh_file6(self):
+        dict = self.tri_dict.copy()
+        fileName = tempfile.mktemp(".msh")
+        export_mesh_file(fileName,dict)
+        loaded_dict = loadASCII._read_msh_file(fileName)
+        os.remove(fileName)
+        dict = self.tri_dict
+        #print "*********************"
+        #print dict
+        #print "**loaded_dict*******************"
+        #print loaded_dict
+        #print "*********************" 
+        self.check_mesh_dicts(loaded_dict,dict, 'test_read_write_msh_fileIII')
+                         
+    def check_mesh_dicts(self, loaded_dict, dict, fail_string ):
+        assert allclose(array(loaded_dict['points']),
+                        array(dict['points']))
+                        
+	assert allclose(array(loaded_dict['point_attributes']),
+                        array(dict['point_attributes']))
+        assert allclose(array(loaded_dict['outline_segments']),
+                        array(dict['outline_segments']))
+        
+        self.failUnless(loaded_dict['outline_segment_tags']  ==
+                        dict['outline_segment_tags'],
+                        fail_string + ' failed!! Test 4')
+        
+            
+        assert allclose(array(loaded_dict['regions']),
+                        array(dict['regions']))
+        self.failUnless(loaded_dict['region_tags']  ==
+                        dict['region_tags'],
+                        fail_string + ' failed!! Test 5')
+        
+        assert allclose(array(loaded_dict['region_max_areas']),
+                        array(dict['region_max_areas']))
+ 
+        assert allclose(array(loaded_dict['holes']),
+                        array(dict['holes']))
+ 
+        assert allclose(array(dict['vertices']),
+                        array(loaded_dict['vertices']))
+ 
+        assert allclose(array(dict['triangles']),
+                        array(loaded_dict['triangles']))
+ 
+        assert allclose(array(dict['segments']),
+                        array(loaded_dict['segments']))
+        for ob, ldob in map(None,dict['triangle_tags'],
+                              loaded_dict['triangle_tags']):
+            self.failUnless(ob  == ldob,
+                        fail_string + ' failed!! Test triangle_tags')
+        self.failUnless(loaded_dict['vertex_attributes']  ==
+                        dict['vertex_attributes'],
+                        fail_string + ' failed!! Test vertex_attributes')
+ 
+        assert allclose(array(dict['triangle_neighbors']),
+                        array(loaded_dict['triangle_neighbors']))
+
+        for seg, ldseg in map(None,dict['segment_tags'],
+                              loaded_dict['segment_tags']):
+            self.failUnless(seg  == ldseg,
+                        fail_string + ' failed!! Test 8')
+        try:
+            assert allclose(array(dict['vertex_attribute_titles']),
+                            array(loaded_dict['vertex_attribute_titles']))
+        except TypeError:
+            self.failUnless(array(loaded_dict['vertex_attribute_titles'])  ==
+                        array(dict['vertex_attribute_titles']),
+                        fail_string + ' failed!! Test 8')
+        try:    
+            self.failUnless(loaded_dict['geo_reference']  ==
+                        dict['geo_reference'] ,
+                        fail_string + ' failed!! Test geo_reference')
+        except KeyError:          
+            self.failUnless(not dict.has_key('geo_reference' and
+                         loaded_dict['geo_reference'] == None)   ,
+                        fail_string + ' failed!! Test geo_reference')
+  
+########################## BAD .MSH ##########################          
+
+    def test_load_bad_no_file_msh(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".msh")
+        #print fileName
+        try:
+            dict = import_mesh_file(fileName)
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'imaginary file did not raise error!')
+            
+    def test_import_msh_bad(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".msh")
+        file = open(fileName,"w")
+        #   this is  a bad tsh file
+        file.write("elevn\n\
+1.0 what \n\
+0.0 the \n\
+1.0 !!! \n")
+        file.close()
+        #print fileName
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad msh file did not raise error!')        
+        os.remove(fileName)         
+   
+  ###################### .XYA ##############################
+        
+    def test_export_xya_file(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        dict['geo_reference'] = Geo_reference(56,1.9,1.9)
+        
+        
+        fileName = tempfile.mktemp(".xya")
+        export_points_file(fileName, dict)
+        dict2 = import_points_file(fileName)
+        #print "fileName",fileName 
+        os.remove(fileName)
+        #print "dict2",dict2 
+        
+	assert allclose(dict2['pointlist'],[[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict2['attributelist']['elevation'], [10.0, 0.0, 10.4])
+        answer = [10.0, 0.0, 10.4]
+	assert allclose(dict2['attributelist']['brightness'], answer)
+        #print "dict2['geo_reference']",dict2['geo_reference'] 
+        self.failUnless(dict['geo_reference'] == dict2['geo_reference'],
+                         'test_writepts failed. Test geo_reference')
+
+    def test_export_xya_file2(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        
+        
+        fileName = tempfile.mktemp(".xya")
+        export_points_file(fileName, dict)
+        dict2 = import_points_file(fileName)
+        #print "fileName",fileName 
+        os.remove(fileName)
+        #print "dict2",dict2 
+        
+	assert allclose(dict2['pointlist'],[[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict2['attributelist']['elevation'], [10.0, 0.0, 10.4])
+        answer = [10.0, 0.0, 10.4]
+	assert allclose(dict2['attributelist']['brightness'], answer)
+
+        
+    def test_loadxya(self):
+        """
+        comma delimited
+        """
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("elevation  , speed \n\
+1.0, 0.0, 10.0, 0.0\n\
+0.0, 1.0, 0.0, 10.0\n\
+1.0, 0.0, 10.4, 40.0\n")
+        file.close()
+        #print fileName
+        dict = import_points_file(fileName,delimiter = ',')
+        os.remove(fileName)
+	assert allclose(dict['pointlist'], [[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict['attributelist']['elevation'], [10.0, 0.0, 10.4])
+	assert allclose(dict['attributelist']['speed'], [0.0, 10.0, 40.0])
+
+    #FIXME - redundant test?
+    def test_loadxy(self):
+        """
+        To test the mesh side of loading xya files.
+        Not the loading of xya files
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("elevation speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n")
+        file.close()
+        #print fileName
+        dict = import_points_file(fileName)
+        os.remove(fileName)
+	assert allclose(dict['pointlist'], [[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict['attributelist']['elevation'], [10.0, 0.0, 10.4])
+	assert allclose(dict['attributelist']['speed'], [0.0, 10.0, 40.0])
+
+               
+    def test_loadxya2(self):
+        """
+        space delimited
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation   speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n")
+        file.close()
+        #print fileName
+        dict = import_points_file(fileName,delimiter = ' ')
+        os.remove(fileName)
+	assert allclose(dict['pointlist'], [[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict['attributelist']['elevation'], [10.0, 0.0, 10.4])
+	assert allclose(dict['attributelist']['speed'], [0.0, 10.0, 40.0])
+     
+    def test_loadxya3(self):
+        """
+        space delimited
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation   speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n\
+#geocrap\n\
+56\n\
+56.6\n\
+3\n")
+        file.close()
+        #print fileName
+        dict = import_points_file(fileName,delimiter = ' ')
+        os.remove(fileName)
+	assert allclose(dict['pointlist'], [[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict['attributelist']['elevation'], [10.0, 0.0, 10.4])
+	assert allclose(dict['attributelist']['speed'], [0.0, 10.0, 40.0])
+     
+        geo_reference = Geo_reference(56, 56.6, 3.0)
+        
+        self.failUnless(geo_reference == dict['geo_reference'],
+                         'test_writepts failed. Test geo_reference')
+        
+########################## BAD .XYA ##########################
+  
+    def test_loadxy_bad_no_file_xya(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        #print fileName
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'imaginary file did not raise error!')
+  
+    def test_read_write_points_file_bad(self):
+        dict = self.tri_dict.copy()
+        fileName = tempfile.mktemp(".xxx")
+        try:
+            export_points_file(fileName,dict)
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad points file extension did not raise error!')
+     
+    def test_read_write_points_file_bad2(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        dict['geo_reference'] = Geo_reference(56,1.9,1.9)
+        try:
+            export_points_file("d:/???/yeah.xya",dict)
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad points file extension did not raise error!')
+                   
+    def test_loadxy_bad(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation   \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n")
+        file.close()
+        #print fileName
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad xya file did not raise error!')
+        os.remove(fileName)
+       
+    def test_loadxy_bad2(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("elevation\n\
+1.0 0.0 10.0 \n\
+0.0 1.0\n\
+1.0 \n")
+        file.close()
+        #print fileName
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad xya file did not raise error!')    
+        os.remove(fileName)
+   
+    def test_loadxy_bad3(self):
+        """
+        specifying wrong delimiter
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation  , speed \n\
+1.0, 0.0, 10.0, 0.0\n\
+0.0, 1.0, 0.0, 10.0\n\
+1.0, 0.0, 10.4, 40.0\n")
+        file.close()
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad xya file did not raise error!')    
+        os.remove(fileName)
+     
+    def test_loadxy_bad4(self):
+        """
+        specifying wrong delimiter
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation   speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n\
+yeah")
+        file.close()
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad xya file did not raise error!')    
+        os.remove(fileName)
+  
+    def test_loadxy_bad4(self):
+        """
+        specifying wrong delimiter
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation   speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n\
+#geocrap")
+        file.close()
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad xya file did not raise error!')    
+        os.remove(fileName)
+
+    def test_loadxy_bad5(self):
+        """
+        specifying wrong delimiter
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("  elevation   speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n\
+#geocrap\n\
+crap")
+        file.close()
+        try:
+            dict = import_points_file(fileName,delimiter = ' ')
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad xya file did not raise error!')    
+        os.remove(fileName)        
+  ############### .PTS ##########
+            
+    def test_loadpts(self):
+        
+        from Scientific.IO.NetCDF import NetCDFFile
+
+        fileName = tempfile.mktemp(".pts")
+        # NetCDF file definition
+        outfile = NetCDFFile(fileName, 'w')
+        
+        # dimension definitions
+        outfile.createDimension('number_of_points', 3)    
+        outfile.createDimension('number_of_dimensions', 2) #This is 2d data
+    
+        # variable definitions
+        outfile.createVariable('points', Float, ('number_of_points',
+                                                 'number_of_dimensions'))
+        outfile.createVariable('elevation', Float, ('number_of_points',))
+    
+        # Get handles to the variables
+        points = outfile.variables['points']
+        elevation = outfile.variables['elevation']
+ 
+        points[0, :] = [1.0,0.0]
+        elevation[0] = 10.0 
+        points[1, :] = [0.0,1.0]
+        elevation[1] = 0.0  
+        points[2, :] = [1.0,0.0]
+        elevation[2] = 10.4    
+
+        outfile.close()
+        
+        dict = import_points_file(fileName)
+        os.remove(fileName)
+        answer =  [[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]]
+        #print "dict['pointlist']",dict['pointlist']
+        #print "answer",answer
+	assert allclose(dict['pointlist'], [[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict['attributelist']['elevation'], [10.0, 0.0, 10.4])
+ 
+    def test_writepts(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        dict['geo_reference'] = Geo_reference(56,1.9,1.9)
+        
+        
+        fileName = tempfile.mktemp(".pts")
+        export_points_file(fileName, dict)
+        dict2 = import_points_file(fileName)
+        #print "fileName",fileName 
+        os.remove(fileName)
+        #print "dict2",dict2 
+        
+	assert allclose(dict2['pointlist'],[[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	assert allclose(dict2['attributelist']['elevation'], [10.0, 0.0, 10.4])
+        answer = [10.0, 0.0, 10.4]
+	assert allclose(dict2['attributelist']['brightness'], answer)
+
+        #print "dict['geo_reference'] ",dict['geo_reference'] 
+        #print "dict2['geo_reference']",dict2['geo_reference']
+        
+        self.failUnless(dict['geo_reference'] == dict2['geo_reference'],
+                         'test_writepts failed. Test geo_reference')
+        
+ ########################## BAD .PTS ##########################          
+
+    def test_load_bad_no_file_pts(self):
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".pts")
+        #print fileName
+        try:
+            dict = import_points_file(fileName)
+        except IOError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'imaginary file did not raise error!')
+            
+  ############### .PTS OTHER ##########
+
+    def test_concatinate_attributelist(self):
+        dic = {}
+        dic['one'] = array([1,2])
+        dic['2'] = array([2,7])
+        dic['three'] = array([3,79])
+        dic['4'] = array([4,47])
+        dic['five'] = array([5,17])
+        titles, block = concatinate_attributelist(dic)
+        #print "titles", titles
+        #print "array", block
+        self.failUnless(titles  == ['4', '2', 'five', 'three', 'one'],
+                         'test_concatinate_attributelist failed.')
+	assert allclose(block, [[4,2,5,3,1],[47,7,17,79,2]])
+
+    def test_half_pts(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        out_dict = half_pts(dict)
+	assert allclose(out_dict['pointlist'],[[1.0, 0.0],[1.0, 0.0]])
+	assert allclose(out_dict['attributelist']['elevation'], [10.0, 10.4])
+	assert allclose(out_dict['attributelist']['brightness'], [10.0, 10.4])
+  
+    def test_extent_point_atts(self):
+        
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 10.0],[0.0, 1.0],[10.0, -10.0]])
+	att_dict['elevation'] = array([30.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        out_dict = extent_point_atts(dict)
+        
+        #print "out_dict['pointlist']",out_dict #['pointlist'] 
+	assert allclose(out_dict['pointlist'],[[0.0, -10.0],[10.0, -10.0],
+                                               [10.0,10.0],[0.0, 10.0]])
+
+        self.failUnless(dict['attributelist']  == {},
+                         'test_extent_point_atts failed. Test 1')
+        
+    def test_reduce_pts(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        
+        inFileName = tempfile.mktemp(".pts")
+        export_points_file(inFileName, dict)
+        
+        outFileName = tempfile.mktemp(".pts")
+        
+        dict2 = reduce_pts(inFileName,outFileName, 1 )
+        os.remove(inFileName)
+
+        dict2 = import_points_file(outFileName)
+        os.remove(outFileName)
+        #print "dict2",dict2 
+        
+	assert allclose(dict2['pointlist'],[[1.0, 0.0]])
+	assert allclose(dict2['attributelist']['elevation'], [10.0])
+	assert allclose(dict2['attributelist']['brightness'], [10.0])
+       
+    def test_produce_half_point_files(self):
+        dict = {}
+        att_dict = {}
+	dict['pointlist'] = array([[1.0, 0.0],[0.0, 1.0],[1.0, 0.0]])
+	att_dict['elevation'] = array([10.0, 0.0, 10.4])
+	att_dict['brightness'] = array([10.0, 0.0, 10.4])
+        dict['attributelist'] = att_dict
+        
+        inFileName = tempfile.mktemp(".pts")
+        export_points_file(inFileName, dict)
+        
+        outFileName = tempfile.mktemp(".pts")
+        delimiter = '_p'
+        outfiles = produce_half_point_files(inFileName, 1,
+                                            delimiter, verbose = False )
+        os.remove(inFileName)
+        root, ext = splitext(inFileName)
+        outFileName = root + delimiter + ext
+        #print "outFileName",outfiles  
+        dict2 = import_points_file(outfiles[1])
+        for file in outfiles:
+            #print "del file",file 
+            os.remove(file)
+        
+	assert allclose(dict2['pointlist'],[[1.0, 0.0]])
+	assert allclose(dict2['attributelist']['elevation'], [10.0])
+	assert allclose(dict2['attributelist']['brightness'], [10.0])
+                      
+#-------------------------------------------------------------
+if __name__ == "__main__":
+
+    suite = unittest.makeSuite(loadASCIITestCase,'test')
+    #suite = unittest.makeSuite(loadASCIITestCase,'test_writepts')
+    runner = unittest.TextTestRunner() #verbosity=2)
+    runner.run(suite)
+    
Index: /inundation/pmesh/mesh.py
===================================================================
--- /inundation/pmesh/mesh.py	(revision 1719)
+++ /inundation/pmesh/mesh.py	(revision 1719)
@@ -0,0 +1,3540 @@
+#!/usr/bin/env python
+#
+"""General 2D triangular classes for triangular mesh generation.
+
+   Note: A .index attribute is added to objects such as vertices and
+   segments, often when reading and writing to files.  This information
+   should not be used as percistant information.  It is not the 'index' of
+   an element in a list.
+
+   
+   Copyright 2003/2004
+   Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
+   Geoscience Australia
+"""
+import load_mesh.loadASCII
+import alpha_shape.alpha_shape
+
+import sys
+import math
+import triang
+import re
+import os
+import pickle
+
+import types
+import exceptions
+
+import load_mesh
+from coordinate_transforms.geo_reference import Geo_reference,DEFAULT_ZONE
+
+SET_COLOUR='red'
+    
+def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2):
+    """
+    """
+    
+    det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0)            
+    a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0)
+    a /= det
+
+    b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0)
+    b /= det            
+
+    return a, b
+
+##############################################
+#Initialise module
+
+import compile
+if compile.can_use_C_extension('util_ext.c'):
+    from util_ext import gradient, point_on_line 
+else:
+    gradient = gradient_python
+
+# 1st and third values must be the same
+# FIXME: maybe make this a switch that the user can change? - DSG
+initialconversions = ['', 'exterior','']
+
+#from os import sep
+#sys.path.append('..'+sep+'pmesh')
+#print "sys.path",sys.path
+
+class MeshObject:
+    """
+    An abstract superclass for the basic mesh objects, eg vertex, segment,
+    triangle.
+    """
+    def __init__(self):
+        pass
+    
+class Point(MeshObject): 
+    """
+    Define a point in a 2D space.
+    """
+    def __init__(self,X,Y):
+        __slots__ = ['x','y']
+        self.x=X
+        self.y=Y
+        
+    def DistanceToPoint(self, OtherPoint):
+        """
+        Returns the distance from this point to another
+        """
+        SumOfSquares = ((self.x - OtherPoint.x)**2) + ((self.y - OtherPoint.y)**2)
+        return math.sqrt(SumOfSquares)
+        
+    def IsInsideCircle(self, Center, Radius):
+        """
+        Return 1 if this point is inside the circle, 
+        0 otherwise
+        """
+        
+        if (self.DistanceToPoint(Center)<Radius):
+            return 1
+        else:
+            return 0
+        
+    def __repr__(self):
+        return "(%f,%f)" % (self.x,self.y) 
+
+    def cmp_xy(self, point):
+        if self.x < point.x:
+            return -1
+        elif self.x > point.x:
+            return 1
+        else:           
+            if self.y < point.y:
+                return -1
+            elif self.y > point.y:
+                return 1
+            else:
+                return 0
+        
+    def same_x_y(self, point):
+        if self.x == point.x and self.y == point.y:
+            return True
+        else:
+            return False
+        
+            
+
+class Vertex(Point):
+    """
+    A point on the mesh.
+    Object attributes based on the Triangle program
+    """
+    def __init__(self,X,Y, attributes = None):
+        __slots__ = ['x','y','attributes']
+        
+        assert (type(X) == types.FloatType or type(X) == types.IntType)
+        assert (type(Y) == types.FloatType or type(Y) == types.IntType)
+        self.x=X
+        self.y=Y        
+        self.attributes=[] 
+        
+        if attributes is None:
+            self.attributes=[]
+        else:
+            self.attributes=attributes
+    
+
+    def setAttributes(self,attributes):
+        """
+        attributes is a list.
+        """
+        self.attributes = attributes
+        
+    VERTEXSQUARESIDELENGTH = 6
+    def draw(self, canvas, tags, colour = 'black',scale = 1, xoffset = 0, yoffset =0, ):
+        x =  scale*(self.x + xoffset)
+        y = -1*scale*(self.y + yoffset)  # - since for a canvas - is up
+        #print "draw x:", x
+        #print "draw y:", y
+        cornerOffset= self.VERTEXSQUARESIDELENGTH/2
+
+        # A hack to see the vert tags
+        # note: there will be many tags, since tags will not be removed
+        #when zooming
+        #canvas.create_text(x+ 2*cornerOffset,
+        #                   y+ 2*cornerOffset,
+        #                        text=tags)
+        
+        return canvas.create_rectangle(x-cornerOffset,
+                                       y-cornerOffset,
+                                       x+cornerOffset,
+                                       y+cornerOffset,
+                                       tags = tags,
+                                       outline=colour,
+                                       fill = 'white')
+    
+        #return tags
+     
+    def __repr__(self):
+        return "[(%f,%f),%r]" % (self.x,self.y,self.attributes)
+    
+class Hole(Point):
+    """
+    A region of the mesh were no triangles are generated.
+    Defined by a point in the hole enclosed by segments.
+    """
+    HOLECORNERLENGTH = 6
+    def draw(self, canvas, tags, colour = 'purple',scale = 1, xoffset = 0, yoffset =0, ):
+        x =  scale*(self.x + xoffset)
+        y = -1*scale*(self.y + yoffset)  # - since for a canvas - is up
+        #print "draw x:", x
+        #print "draw y:", y
+        cornerOffset= self.HOLECORNERLENGTH/2
+        return canvas.create_oval(x-cornerOffset,
+                                       y-cornerOffset,
+                                       x+cornerOffset,
+                                       y+cornerOffset,
+                                       tags = tags,
+                                       outline=colour,
+                                       fill = 'white')
+    
+class Region(Point):
+    """ 
+    A region of the mesh, defined by a point in the region
+    enclosed by segments. Used to tag areas.
+    """
+    CROSSLENGTH = 6
+    TAG = 0
+    MAXAREA = 1
+    
+    def __init__(self,X,Y, tag = None, maxArea = None):
+        """Precondition: tag is a string and maxArea is a real
+        """
+        # This didn't work.  
+        #super(Region,self)._init_(self,X,Y)
+        self.x=X
+        self.y=Y    
+        self.attributes =[] # index 0 is the tag string
+                            #optoinal index 1 is the max triangle area
+                            #NOTE the size of this attribute is assumed
+                            # to be 1 or 2 in regionstrings2int
+        if tag is None:
+            self.attributes.append("")
+        else:
+            self.attributes.append(tag) #this is a string
+            
+        if maxArea is not None:
+            self.setMaxArea(maxArea) # maxArea is a number
+            
+    def getTag(self,):
+        return self.attributes[self.TAG]
+    
+    def setTag(self,tag):
+        self.attributes[self.TAG] = tag
+        
+    def getMaxArea(self):
+        """ Returns the Max Area of a Triangle or
+        None, if the Max Area has not been set.
+        """
+        if self.isMaxArea():
+            return self.attributes[1]
+        else:
+            return None
+    
+    def setMaxArea(self,MaxArea):
+        if self.isMaxArea(): 
+            self.attributes[self.MAXAREA] = float(MaxArea)
+        else:
+            self.attributes.append( float(MaxArea) )
+    
+    def deleteMaxArea(self):
+        if self.isMaxArea():
+            self.attributes.pop(self.MAXAREA)
+            
+    def isMaxArea(self):
+        return len(self.attributes)> 1
+    
+    def draw(self, canvas, tags, scale=1, xoffset = 0, yoffset =0, colour = "black"):
+        """
+        Draw a black cross, returning the objectID
+        """
+        x =  scale*(self.x + xoffset)
+        y = -1*scale*(self.y + yoffset) 
+        cornerOffset= self.CROSSLENGTH/2
+        return canvas.create_polygon(x,
+                                     y-cornerOffset,
+                                     x,
+                                     y,
+                                     x+cornerOffset,
+                                     y,
+                                     x,
+                                     y,
+                                     x,
+                                     y+cornerOffset,
+                                     x,
+                                     y,
+                                     x-cornerOffset,
+                                     y,
+                                     x,
+                                     y,
+                                     tags = tags,
+                                     outline = colour,fill = '')
+    
+    def __repr__(self):
+        if self.isMaxArea():
+            area = self.getMaxArea() 
+            return "(%f,%f,%s,%f)" % (self.x,self.y,
+                                      self.getTag(), self.getMaxArea())
+        else:
+            return "(%f,%f,%s)" % (self.x,self.y,
+                                   self.getTag())
+        
+class Triangle(MeshObject):
+    """
+    A triangle element, defined by 3 vertices.
+    Attributes based on the Triangle program.
+    """
+
+    def __init__(self, vertex1, vertex2, vertex3, attribute = None, neighbors = None ):
+        """
+        Vertices, the initial arguments, are listed in counterclockwise order.
+        """
+        self.vertices= [vertex1,vertex2, vertex3 ]
+        
+        if attribute is None:
+            self.attribute =""
+        else:
+            self.attribute = attribute #this is a string
+            
+        if neighbors is None:
+            self.neighbors=[]
+        else:
+            self.neighbors=neighbors
+
+    def replace(self,new_triangle):
+        self = new_triangle
+
+    def longestSideID(self):
+        ax = self.vertices[0].x
+        ay = self.vertices[0].y
+        
+        bx = self.vertices[1].x
+        by = self.vertices[1].y
+        
+        cx = self.vertices[2].x
+        cy = self.vertices[2].y
+
+        lenA = ((cx-bx)**2+(cy-by)**2)**0.5
+        lenB = ((ax-cx)**2+(ay-cy)**2)**0.5
+        lenC = ((bx-ax)**2+(by-ay)**2)**0.5
+ 
+        len = [lenA,lenB,lenC]
+        return len.index(max(len))
+
+    def rotate(self,offset):
+        """
+        permute the order of the sides of the triangle
+        offset must be 0,1 or 2
+        """
+
+        if offset == 0:
+            pass
+        else:
+            if offset == 1:
+                self.vertices = [self.vertices[1],self.vertices[2],self.vertices[0]]
+                self.neighbors = [self.neighbors[1],self.neighbors[2],self.neighbors[0]]
+            if offset == 2:
+                self.vertices = [self.vertices[2],self.vertices[0],self.vertices[1]]
+                self.neighbors = [self.neighbors[2],self.neighbors[0],self.neighbors[1]]
+
+    def rotate_longest_side(self):
+        self.rotate(self.longestSideID())
+
+    def getVertices(self):
+        return self.vertices
+    
+    def calcArea(self):
+        ax = self.vertices[0].x
+        ay = self.vertices[0].y
+        
+        bx = self.vertices[1].x
+        by = self.vertices[1].y
+        
+        cx = self.vertices[2].x
+        cy = self.vertices[2].y
+        
+        return abs((bx*ay-ax*by)+(cx*by-bx*cy)+(ax*cy-cx*ay))/2
+    
+    def calcP(self):
+        #calculate the perimeter
+        ax = self.vertices[0].x
+        ay = self.vertices[0].y
+        
+        bx = self.vertices[1].x
+        by = self.vertices[1].y
+        
+        cx = self.vertices[2].x
+        cy = self.vertices[2].y
+
+        a =  ((cx-bx)**2+(cy-by)**2)**0.5 
+        b =  ((ax-cx)**2+(ay-cy)**2)**0.5 
+        c =  ((bx-ax)**2+(by-ay)**2)**0.5 
+
+        return a+b+c
+            
+    def setNeighbors(self,neighbor1 = None, neighbor2 = None, neighbor3 = None):
+        """
+        neighbor1 is the triangle opposite vertex1 and so on.
+        Null represents no neighbor
+        """
+        self.neighbors = [neighbor1, neighbor2, neighbor3]
+
+    def setAttribute(self,attribute):
+        """
+        neighbor1 is the triangle opposite vertex1 and so on.
+        Null represents no neighbor
+        """
+        self.attribute = attribute
+        
+    def __repr__(self):
+        return "[%s,%s]" % (self.vertices,self.attribute)
+        
+
+    def draw(self, canvas, tags, scale=1, xoffset = 0, yoffset =0, colour = "green"):
+        """
+        Draw a triangle, returning the objectID
+        """
+        return canvas.create_polygon(scale*(self.vertices[1].x + xoffset),
+                                     scale*-1*(self.vertices[1].y + yoffset),
+                                     scale*(self.vertices[0].x + xoffset),
+                                     scale*-1*(self.vertices[0].y + yoffset),
+                                     scale*(self.vertices[2].x + xoffset),
+                                     scale*-1*(self.vertices[2].y + yoffset),
+                                     tags = tags,
+                                     outline = colour,fill = '')
+
+class Segment(MeshObject):
+    """
+    Segments are edges whose presence in the triangulation is enforced.
+    
+    """
+    def __init__(self, vertex1, vertex2, tag = None ):
+        """
+        Each segment is specified by listing the vertices of its endpoints
+        The vertices are Vertex objects
+        """
+        assert(vertex1 != vertex2)
+        self.vertices = [vertex1,vertex2 ]
+        
+        if tag is None:
+            self.tag = self.__class__.default
+        else:
+            self.tag = tag #this is a string 
+        
+    def __repr__(self):
+        return "[%s,%s]" % (self.vertices,self.tag)
+            
+        
+    def draw(self, canvas, tags,scale=1 , xoffset=0 , yoffset=0,colour='blue' ):
+        x=[]
+        y=[]
+        for end in self.vertices:
+            #end.draw(canvas,scale, xoffset, yoffset ) # draw the vertices
+            x.append(scale*(end.x + xoffset))
+            y.append(-1*scale*(end.y + yoffset)) # - since for a canvas - is up
+        
+        return canvas.create_line(x[0], y[0], x[1], y[1],
+                                  tags = tags,fill=colour)
+    def set_tag(self,tag):
+        self.tag = tag
+        
+    # Class methods
+    def set_default_tag(cls, default):
+        cls.default = default 
+    
+    def get_default_tag(cls):
+        return cls.default
+    
+    set_default_tag = classmethod(set_default_tag)  
+    get_default_tag = classmethod(get_default_tag)
+
+Segment.set_default_tag("")       
+
+class Mesh:
+    """
+    Representation of a 2D triangular mesh.
+    User attributes describe the mesh region/segments/vertices/attributes
+
+    mesh attributes describe the mesh that is produced eg triangles and vertices.
+    
+    The Mesh holds user information to define the 
+    """
+
+    def __repr__(self):
+        return """
+        mesh Triangles: %s 
+        mesh Attribute Titles: %s 
+        mesh Segments: %s  
+        mesh Vertices: %s 
+        user Segments: %s  
+        user Vertices: %s  
+        holes: %s 
+        regions: %s""" % (self.meshTriangles,
+                                self.attributeTitles,
+                                self.meshSegments,
+                                self.meshVertices,
+                                self.getUserSegments(),
+                                self.userVertices,
+                                self.holes,
+                                self.regions) 
+    
+    def __init__(self,
+                 userSegments=None,
+                 userVertices=None,
+                 holes=None,
+                 regions=None,
+                 geo_reference=None):
+        self.meshTriangles=[] 
+        self.attributeTitles=[] 
+        self.meshSegments=[]
+        self.meshVertices=[]
+
+        #Peters stuff
+        # FIXME (DSG) Sets of what?  
+        self.setID={}
+        #a dictionary of names.
+        #multiple sets are allowed, but the gui does not yet
+        #support this
+        
+        self.setID['None']=0
+        #contains the names of the sets pointing to the indexes
+        #in the list. 
+        
+	self.sets=[[]]
+        #Contains the lists of triangles (triangle sets)
+
+       
+        self.visualise_graph = True
+
+        if userSegments is None:
+            self.userSegments=[]
+        else:
+            self.userSegments=userSegments
+        self.alphaUserSegments=[]
+            
+        if userVertices is None:
+            self.userVertices=[]
+        else:
+            self.userVertices=userVertices
+            
+        if holes is None:
+            self.holes=[]
+        else:
+            self.holes=holes
+            
+        if regions is None:
+            self.regions=[]
+        else:
+            self.regions=regions
+
+        if geo_reference is None:
+            self.geo_reference = Geo_reference(DEFAULT_ZONE,0,0) 
+        else:
+            self.geo_reference = geo_reference
+            
+    def __cmp__(self,other):
+        
+        # A dic for the initial m
+        dic = self.Mesh2triangList()
+        dic_mesh = self.Mesh2MeshList()
+        for element in dic_mesh.keys():
+            dic[element] = dic_mesh[element]
+        for element in dic.keys():
+            dic[element].sort()
+            
+        # A dic for the exported/imported m
+        dic_other = other.Mesh2triangList()
+        dic_mesh = other.Mesh2MeshList()
+        for element in dic_mesh.keys():
+            dic_other[element] = dic_mesh[element]
+        for element in dic.keys():
+            dic_other[element].sort()
+
+        #print "dsg************************8"
+        #print "dic ",dic
+        #print "*******8"
+        #print "mesh",dic_other
+        #print "dic.__cmp__(dic_o)",dic.__cmp__(dic_other)
+        #print "dsg************************8"
+        
+        return (dic.__cmp__(dic_other))
+        
+    def generateMesh(self, mode = None, maxArea = None, isRegionalMaxAreas = True):
+        """
+        Based on the current user vaules, holes and regions
+        generate a new mesh
+        mode is a string that sets conditions on the mesh generations
+        see triangle_instructions.txt for a definition of the commands
+        PreCondition: maxArea is a double
+        """
+        #print "mode ",mode
+        if mode == None:
+            self.mode = ""
+        else:
+            self.mode = mode
+        
+        if not re.match('p',self.mode):
+            self.mode += 'p' #p - read a planar straight line graph.
+            #there must be segments to use this switch
+            # TODO throw an aception if there aren't seg's
+            # it's more comlex than this.  eg holes
+        if not re.match('z',self.mode):
+            self.mode += 'z' # z - Number all items starting from zero (rather than one)
+        if not re.match('n',self.mode):
+            self.mode += 'n' # n - output a list of neighboring triangles 
+        if not re.match('A',self.mode):
+            self.mode += 'A' # A - output region attribute list for triangles
+        if not re.match('V',self.mode) and not re.match('Q',self.mode):
+            self.mode += 'V' # V - output info about what Triangle is doing
+        
+        if maxArea != None:
+            self.mode += 'a' + str(maxArea)
+
+        if isRegionalMaxAreas:
+            self.mode += 'a'
+            
+        meshDict = self.Mesh2triangList()
+        #print "!@!@ This is going to triangle   !@!@"
+        #print meshDict
+        #print "!@!@ This is going to triangle   !@!@"
+
+        #print "meshDict['segmenttaglist']", meshDict['segmenttaglist']
+        [meshDict['segmenttaglist'],
+         segconverter] =  segment_strings2ints(meshDict['segmenttaglist'],
+                                             initialconversions)
+        #print "regionlist",meshDict['regionlist']
+        [meshDict['regionlist'],
+         regionconverter] =  region_strings2ints(meshDict['regionlist'])
+        #print "regionlist",meshDict['regionlist']
+        #print "meshDict['segmenttaglist']", meshDict['segmenttaglist'
+        generatedMesh = triang.genMesh(
+                              meshDict['pointlist'],
+                              meshDict['segmentlist'],
+                              meshDict['holelist'],
+                              meshDict['regionlist'],
+                              meshDict['pointattributelist'],
+                              meshDict['segmenttaglist'],
+                              [],  # since the trianglelist isn't used
+                              self.mode)
+        #print "generated",generatedMesh['generatedsegmenttaglist']
+        generatedMesh['generatedsegmentmarkerlist'] = \
+                     segment_ints2strings(generatedMesh['generatedsegmentmarkerlist'],
+                                  segconverter)
+        #print "processed gen",generatedMesh['generatedsegmentmarkerlist']
+        generatedMesh['generatedtriangleattributelist'] = \
+         region_ints2strings(generatedMesh['generatedtriangleattributelist'],
+                                  regionconverter)
+
+
+        if len(generatedMesh['generatedpointattributelist'][0])==0:
+            self.attributeTitles = []
+        generatedMesh['generatedpointattributetitlelist']=self.attributeTitles     
+
+        self.setTriangulation(generatedMesh)
+    
+    def addUserPoint(self, pointType, x,y):
+        if pointType == Vertex:
+            point = self.addUserVertex(x,y)
+        if pointType == Hole:
+            point = self.addHole(x,y)
+        if pointType == Region:
+            point = self.addRegion(x,y)
+        return point
+    
+    def addUserVertex(self, x,y):
+        v=Vertex(x, y)
+        self.userVertices.append(v)
+        return v
+
+    def addHole(self, x,y):
+        h=Hole(x, y)
+        self.holes.append(h)
+        return h
+   
+    def addRegion(self, x,y):
+        h=Region(x, y)
+        self.regions.append(h)
+        return h
+    
+    def addRegionEN(self, x,y):
+        h=Region(x-self.geo_reference.xllcorner,
+                 y-self.geo_reference.yllcorner)
+        self.regions.append(h)
+        return h
+    
+    def getUserVertices(self):
+        return self.userVertices
+    
+    def getUserSegments(self):
+        allSegments = self.userSegments + self.alphaUserSegments
+        #print "self.userSegments",self.userSegments
+        #print "self.alphaUserSegments",self.alphaUserSegments
+        #print "allSegments",allSegments
+        return allSegments
+    
+    def deleteUserSegments(self,seg):
+        if self.userSegments.count(seg) == 0:
+            self.alphaUserSegments.remove(seg)
+            pass
+        else:
+            self.userSegments.remove(seg)
+            
+    def clearUserSegments(self):
+        self.userSegments = []
+        self.alphaUserSegments = []
+                
+    def getTriangulation(self):
+        return self.meshTriangles
+    
+    def getMeshVertices(self):
+        return self.meshVertices
+ 
+    def getMeshSegments(self):
+        return self.meshSegments
+    
+    def getHoles(self):
+        return self.holes
+    
+    def getRegions(self):
+        return self.regions
+    
+    def isTriangulation(self):
+        if self.meshVertices == []:
+            return False 
+        else:
+            return True
+    
+    def addUserSegment(self, v1,v2):
+        """
+        PRECON: A segment between the two vertices is not already present.
+        Check by calling isUserSegmentNew before calling this function.
+        
+        """
+        s=Segment( v1,v2)
+        self.userSegments.append(s)
+        return s
+    
+    def clearTriangulation(self):
+
+        #Clear the current generated mesh values
+        self.meshTriangles=[] 
+        self.meshSegments=[]
+        self.meshVertices=[]
+
+    def removeDuplicatedUserVertices(self):
+        """Pre-condition: There are no user segments
+        This function will keep the first duplicate
+        """
+        assert self.getUserSegments() == []
+        self.userVertices, counter =  self.removeDuplicatedVertices(self.userVertices)
+        return counter
+    
+    def removeDuplicatedVertices(self, Vertices):
+        """
+        This function will keep the first duplicate, remove all others
+        Precondition: Each vertex has a dupindex, which is the list
+        index.
+
+        Note: this removes vertices that have the same x,y values,
+        not duplicate instances in the Vertices list.
+        """
+        remove = []
+        index = 0
+        for v in Vertices:
+            v.dupindex = index
+            index += 1
+        t = list(Vertices)
+        t.sort(Point.cmp_xy)
+    
+        length = len(t)
+        behind = 0
+        ahead  = 1
+        counter = 0
+        while ahead < length:
+            b = t[behind]
+            ah = t[ahead]
+            if (b.y == ah.y and b.x == ah.x):
+                remove.append(ah.dupindex) 
+            behind += 1
+            ahead += 1
+
+        # remove the duplicate vertices
+        remove.sort()
+        remove.reverse()
+        for i in remove:
+            Vertices.pop(i)
+            pass
+
+        #Remove the attribute that this function added
+        for v in Vertices:
+            del v.dupindex
+        return Vertices,counter
+    
+    def thinoutVertices(self, delta):
+        """Pre-condition: There are no user segments
+        This function will keep the first duplicate
+        """
+        assert self.getUserSegments() == []
+        #t = self.userVertices
+        #self.userVertices =[]
+        boxedVertices = {}
+        thinnedUserVertices =[]
+        delta = round(delta,1)
+        
+        for v in self.userVertices :
+            # tag is the center of the boxes
+            tag = (round(v.x/delta,0)*delta,round(v.y/delta,0)*delta)
+            #this creates a dict of lists of faces, indexed by tag
+            boxedVertices.setdefault(tag,[]).append(v)
+
+        for [tag,verts] in boxedVertices.items():
+            min = delta
+            bestVert = None
+            tagVert = Vertex(tag[0],tag[1])
+            for v in verts:
+                dist = v.DistanceToPoint(tagVert)
+                if (dist<min):
+                    min = dist
+                    bestVert = v
+            thinnedUserVertices.append(bestVert)
+        self.userVertices =thinnedUserVertices
+        
+            
+    def isUserSegmentNew(self, v1,v2):
+        identicalSegs= [x for x in self.getUserSegments() if (x.vertices[0] == v1 and x.vertices[1] == v2) or (x.vertices[0] == v2 and x.vertices[1] == v1) ]
+        
+        return len(identicalSegs) == 0
+
+        
+    def deleteSegsOfVertex(self, delVertex):
+        """
+        Delete this vertex and any segments that connect to it.
+        """
+        #Find segments that connect to delVertex
+        deletedSegments = []
+        for seg in self.getUserSegments():
+            if (delVertex in seg.vertices):
+                deletedSegments.append(seg)
+        # Delete segments that connect to delVertex
+        for seg in deletedSegments:
+            self.deleteUserSegments(seg)
+        return deletedSegments
+
+    
+    def deleteMeshObject(self, MeshObject):
+        """
+        Returns a list of all objects that were removed
+        """
+        deletedObs = []
+        if isinstance(MeshObject, Vertex ):
+            deletedObs = self.deleteSegsOfVertex(MeshObject)
+            deletedObs.append(MeshObject)
+            self.userVertices.remove(MeshObject)
+        elif isinstance(MeshObject, Segment):
+            deletedObs.append(MeshObject)
+            self.deleteUserSegments(MeshObject)
+        elif isinstance(MeshObject, Hole):
+            deletedObs.append(MeshObject)
+            self.holes.remove(MeshObject)
+        elif isinstance(MeshObject, Region):
+            deletedObs.append(MeshObject)
+            self.regions.remove(MeshObject)          
+        return deletedObs
+                                                 
+    def Mesh2triangList(self, userVertices=None,
+                        userSegments=None,
+                        holes=None,
+                        regions=None):
+        """
+        Convert the Mesh to a dictionary of the lists needed for the triang modul;
+        points list: [(x1,y1),(x2,y2),...] (Tuples of doubles)
+        pointattributelist: [(a11,a12,...),(a21,a22),...] (Tuples of doubles)
+        segment list: [(point1,point2),(p3,p4),...] (Tuples of integers) 
+        hole list: [(x1,y1),...](Tuples of doubles, one inside each hole region)
+        regionlist: [ (x1,y1,tag, max area),...] (Tuple of 3-4 doubles)
+        
+        Note, this adds an index attribute to the user Vertex objects.
+
+        Used to produce output to triangle
+        """
+        if userVertices is None:
+            userVertices = self.getUserVertices()
+        if userSegments is None:
+            userSegments = self.getUserSegments()
+        if holes is None:
+            holes = self.getHoles()
+        if regions is None:
+            regions = self.getRegions()
+            
+        meshDict = {}
+        
+        pointlist=[]
+        pointattributelist=[]
+        index = 0
+        for vertex in userVertices:
+            vertex.index = index 
+            pointlist.append((vertex.x,vertex.y))
+            pointattributelist.append((vertex.attributes))
+            
+            index += 1
+        meshDict['pointlist'] = pointlist
+        meshDict['pointattributelist'] = pointattributelist
+
+        segmentlist=[]
+        segmenttaglist=[]
+        for seg in userSegments:
+            segmentlist.append((seg.vertices[0].index,seg.vertices[1].index))
+            segmenttaglist.append(seg.tag)
+        meshDict['segmentlist'] =segmentlist 
+        meshDict['segmenttaglist'] =segmenttaglist
+        
+        holelist=[]
+        for hole in holes:
+            holelist.append((hole.x,hole.y)) 
+        meshDict['holelist'] = holelist
+        
+        regionlist=[]
+        for region in regions:
+            if (region.getMaxArea() != None): 
+                regionlist.append((region.x,region.y,region.getTag(),
+                               region.getMaxArea()))
+            else:
+                regionlist.append((region.x,region.y,region.getTag()))
+        meshDict['regionlist'] = regionlist
+        #print "*(*("
+        #print meshDict
+        #print meshDict['regionlist']
+        #print "*(*("
+        return meshDict
+                                                
+    def Mesh2MeshList(self):
+        """
+        Convert the Mesh to a dictionary of lists describing the triangulation variables;
+        generated point list: [(x1,y1),(x2,y2),...] (Tuples of doubles)
+        generated point attribute list: [(a11,a12,...),(a21,a22),...] (Tuples of doubles)
+        generated point attribute title list:[A1Title, A2Title ...] (list of strings)
+        generated segment list: [(point1,point2),(p3,p4),...] (Tuples of integers) 
+        generated segment tag list: [tag,tag,...] list of strings
+
+        generated triangle list: [(p1,p2,p3), (p4,p5,p6),....] tuple of points
+
+        generated triangle attribute list: [s1,s2,...] list of strings
+
+        generated triangle neighbor list: [(t1,t2,t3), (t4,t5,t6),....] tuple of triangles
+        
+        Used to produce .tsh file
+        """
+        
+        meshDict = {}        
+        pointlist=[]
+        pointattributelist=[]
+
+
+        self.maxVertexIndex=0
+        for vertex in self.meshVertices:
+            vertex.index = self.maxVertexIndex 
+            pointlist.append((vertex.x,vertex.y))
+            pointattributelist.append((vertex.attributes))           
+            self.maxVertexIndex += 1
+
+        meshDict['generatedpointlist'] = pointlist
+        meshDict['generatedpointattributelist'] = pointattributelist
+        meshDict['generatedpointattributetitlelist'] = self.attributeTitles
+        #segments
+        segmentlist=[]
+        segmenttaglist=[]
+        for seg in self.meshSegments:
+            segmentlist.append((seg.vertices[0].index,seg.vertices[1].index))
+            segmenttaglist.append(seg.tag)
+        meshDict['generatedsegmentlist'] =segmentlist 
+        meshDict['generatedsegmenttaglist'] =segmenttaglist 
+
+        # Make sure that the indexation is correct
+        index = 0
+        for tri in self.meshTriangles:
+            tri.index = index           
+            index += 1
+
+        trianglelist = []
+        triangleattributelist = []
+        triangleneighborlist = []
+        for tri in self.meshTriangles:  
+            trianglelist.append((tri.vertices[0].index,tri.vertices[1].index,tri.vertices[2].index))  
+            triangleattributelist.append([tri.attribute])
+            neighborlist = [-1,-1,-1]
+            for neighbor,index in map(None,tri.neighbors,
+                                      range(len(tri.neighbors))):
+                if neighbor:    
+                    neighborlist[index] = neighbor.index
+            triangleneighborlist.append(neighborlist)
+        
+        meshDict['generatedtrianglelist'] = trianglelist 
+        meshDict['generatedtriangleattributelist'] = triangleattributelist
+        meshDict['generatedtriangleneighborlist'] = triangleneighborlist
+        
+        #print "mesh.Mesh2MeshList*)*)"
+        #print meshDict
+        #print "mesh.Mesh2MeshList*)*)"
+
+        return meshDict
+
+                               
+    def Mesh2MeshDic(self):
+        """
+        Convert the user and generated info of a mesh to a dictionary
+        structure
+        """
+        dic = self.Mesh2triangList()
+        dic_mesh = self.Mesh2MeshList()
+        for element in dic_mesh.keys():
+            dic[element] = dic_mesh[element]
+        return dic
+    
+    def setTriangulation(self, genDict):
+        """
+        Set the mesh attributes given a dictionary of the lists
+        returned from the triang module       
+        generated point list: [(x1,y1),(x2,y2),...] (Tuples of doubles)  
+        generated point attribute list:[(P1att1,P1attt2, ...),(P2att1,P2attt2,...),...]
+        generated point attribute title list:[A1Title, A2Title ...] (list of strings)
+        generated segment list: [(point1,point2),(p3,p4),...] (Tuples of integers)
+        generated segment marker list: [S1Tag, S2Tag, ...] (list of ints)
+        triangle list:  [(point1,point2, point3),(p5,p4, p1),...] (Tuples of integers)
+        triangle neighbor list: [(triangle1,triangle2, triangle3),(t5,t4, t1),...] (Tuples of integers) -1 means there's no triangle neighbor
+        triangle attribute list: [(T1att), (T2att), ...] (list of a list of strings)
+        """
+        #Clear the current generated mesh values
+        self.meshTriangles=[]
+        self.attributeTitles=[]
+        self.meshSegments=[]
+        self.meshVertices=[]
+
+        #print "mesh.setTriangulation@#@#@#"
+        #print genDict
+        #print "@#@#@#"
+
+        self.maxVertexIndex = 0
+        for point in genDict['generatedpointlist']:
+            v=Vertex(point[0], point[1])
+            v.index =  self.maxVertexIndex
+            self.maxVertexIndex +=1
+            self.meshVertices.append(v)
+
+        self.attributeTitles = genDict['generatedpointattributetitlelist']
+
+        index = 0
+        for seg,marker in map(None,genDict['generatedsegmentlist'],genDict['generatedsegmentmarkerlist']):
+            segObject = Segment( self.meshVertices[seg[0]],
+                           self.meshVertices[seg[1]], tag = marker )
+            segObject.index = index
+            index +=1
+            self.meshSegments.append(segObject)
+
+        index = 0
+        for triangle in genDict['generatedtrianglelist']:
+            tObject =Triangle( self.meshVertices[triangle[0]],
+                        self.meshVertices[triangle[1]],
+                        self.meshVertices[triangle[2]] )
+            tObject.index = index
+            index +=1
+            self.meshTriangles.append(tObject)
+
+        index = 0
+        for att in genDict['generatedtriangleattributelist']:
+            if att == []:
+                self.meshTriangles[index].setAttribute("")
+            else:
+                self.meshTriangles[index].setAttribute(att[0])
+            index += 1
+            
+        index = 0
+        for att in genDict['generatedpointattributelist']:
+            if att == None:
+                self.meshVertices[index].setAttributes([])
+            else:
+                self.meshVertices[index].setAttributes(att)
+            index += 1
+    
+        index = 0
+        for triangle in genDict['generatedtriangleneighborlist']:
+            # Build a list of triangle object neighbors
+            ObjectNeighbor = []
+            for neighbor in triangle:
+                if ( neighbor != -1):
+                    ObjectNeighbor.append(self.meshTriangles[neighbor])
+                else:
+                    ObjectNeighbor.append(None)
+            self.meshTriangles[index].setNeighbors(ObjectNeighbor[0],ObjectNeighbor[1],ObjectNeighbor[2])
+            index += 1
+
+
+    def setMesh(self, genDict):
+        """
+        Set the user Mesh attributes given a dictionary of the lists
+        point list: [(x1,y1),(x2,y2),...] (Tuples of doubles)  
+        point attribute list:[(P1att1,P1attt2, ...),(P2att1,P2attt2,...),...]
+        segment list: [(point1,point2),(p3,p4),...] (Tuples of integers)
+        segment tag list: [S1Tag, S2Tag, ...] (list of ints)
+        region list: [(x1,y1),(x2,y2),...] (Tuples of doubles)
+        region attribute list: ["","reservoir",""] list of strings
+        region max area list:[real, None, Real,...] list of None and reals
+        
+        mesh is an instance of a mesh object
+        """
+        #Clear the current user mesh values
+        self.clearUserSegments()
+        self.userVertices=[]
+        self.Holes=[]
+        self.Regions=[]
+
+        #print "mesh.setMesh@#@#@#"
+        #print genDict
+        #print "@#@#@#"
+        
+        #index = 0
+        for point in genDict['pointlist']:
+            v=Vertex(point[0], point[1])
+            #v.index = index
+            #index +=1
+            self.userVertices.append(v)
+
+        #index = 0
+        for seg,tag in map(None,genDict['segmentlist'],genDict['segmenttaglist']):
+            segObject = Segment( self.userVertices[seg[0]],
+                           self.userVertices[seg[1]], tag = tag )
+            #segObject.index = index
+            #index +=1
+            self.userSegments.append(segObject)
+
+# Remove the loading of attribute info.
+# Have attribute info added using least_squares in pyvolution
+#         index = 0
+#         for att in genDict['pointattributelist']:
+#             if att == None:
+#                 self.userVertices[index].setAttributes([])
+#             else:
+#                 self.userVertices[index].setAttributes(att)
+#            index += 1
+        
+        #index = 0
+        for point in genDict['holelist']:
+            h=Hole(point[0], point[1])
+            #h.index = index
+            #index +=1
+            self.holes.append(h)
+
+        #index = 0
+        for reg,att,maxArea in map(None,
+                                   genDict['regionlist'],
+                                   genDict['regionattributelist'],
+                                   genDict['regionmaxarealist']):
+            Object = Region( reg[0],
+                             reg[1],
+                             tag = att,
+                             maxArea = maxArea)
+            #Object.index = index
+            #index +=1
+            self.regions.append(Object)
+        
+    def addVertsSegs(self, outlineDict):
+        """
+        Add out-line (user Mesh) attributes given a dictionary of the lists
+        points: [(x1,y1),(x2,y2),...] (Tuples of doubles)  
+        segments: [(point1,point2),(p3,p4),...] (Tuples of integers)
+        segment_tags: [S1Tag, S2Tag, ...] (list of strings)
+
+        Assume the values are in Eastings and Northings, with no reference
+        point
+        """
+        if not outlineDict.has_key('segment_tags'):
+            outlineDict['segment_tags'] = []
+            for i in range(len(outlineDict['segments'])):
+                outlineDict['segment_tags'].append('')
+        #print "outlineDict['segment_tags']",outlineDict['segment_tags']
+        #print "outlineDict['points']",outlineDict['points']
+        #print "outlineDict['segments']",outlineDict['segments']
+        
+        localUserVertices = []
+        #index = 0
+        for point in outlineDict['points']:
+            v=Vertex(point[0]-self.geo_reference.xllcorner,
+                     point[1]-self.geo_reference.yllcorner)
+            #v.index = index
+            #index +=1
+            self.userVertices.append(v)
+            localUserVertices.append(v)
+            
+        #index = 0
+        for seg,seg_tag in map(None,outlineDict['segments'],
+                       outlineDict['segment_tags']):
+            segObject = Segment( localUserVertices[seg[0]],
+                           localUserVertices[seg[1]] )
+            if not seg_tag == '':
+                segObject.set_tag(seg_tag)
+            #segObject.index = index
+            #index +=1
+            self.userSegments.append(segObject)
+            #DSG!!!
+            
+    def TestautoSegment(self):
+        newsegs = []
+        s1 = Segment(self.userVertices[0],
+                               self.userVertices[1])
+        s2 = Segment(self.userVertices[0],
+                               self.userVertices[2])
+        s3 = Segment(self.userVertices[2],
+                               self.userVertices[1])
+        if self.isUserSegmentNew(s1.vertices[0],s1.vertices[1]):
+            newsegs.append(s1)
+        if self.isUserSegmentNew(s2.vertices[0],s2.vertices[1]):
+            newsegs.append(s2)
+        if self.isUserSegmentNew(s3.vertices[0],s3.vertices[1]):
+            newsegs.append(s3)
+        #DSG!!!
+        self.userSegments.extend(newsegs)
+        return newsegs
+
+    
+    def savePickle(self, currentName):
+        fd = open(currentName, 'w')
+        pickle.dump(self,fd)
+        fd.close()
+
+    def autoSegmentHull(self):
+        """
+        initially work by running an executable
+        Later compile the c code with a python wrapper.
+
+        Precon: There must be 3 or more vertices in the userVertices structure
+        """
+        newsegs = []
+        inputfile = 'hull_in.txt'
+        outputfile = inputfile + '-alf'
+        #write vertices to file
+        fd = open(inputfile,'w')
+        for v in self.userVertices:
+            fd.write(str(v.x))
+            fd.write(' ')
+            fd.write(str(v.y))
+            fd.write('\n')
+        fd.close()
+        
+        #run hull executable
+        #warning need to compile hull for the current operating system
+        command = 'hull.exe -A -i ' + inputfile
+        os.system(command)
+        
+        #read results into this object
+        fd = open(outputfile)
+        lines = fd.readlines()
+        fd.close()
+        #print "(*(*(*("
+        #print lines
+        #print "(*(*(*("
+        lines.pop(0) #remove the first (title) line
+        for line in lines:
+            vertindexs = line.split()
+            #print 'int(vertindexs[0])', int(vertindexs[0])
+            #print 'int(vertindexs[1])', int(vertindexs[1])
+            #print 'self.userVertices[int(vertindexs[0])]' ,self.userVertices[int(vertindexs[0])]
+            #print 'self.userVertices[int(vertindexs[1])]' ,self.userVertices[int(vertindexs[1])]
+            v1 = self.userVertices[int(vertindexs[0])]
+            v2 = self.userVertices[int(vertindexs[1])]
+            
+            if self.isUserSegmentNew(v1,v2):
+                newseg = Segment(v1, v2)
+                newsegs.append(newseg)
+            #DSG!!!
+        self.userSegments.extend(newsegs)
+        return newsegs
+    def autoSegmentFilter(self,raw_boundary=True,
+                    remove_holes=False,
+                    smooth_indents=False,
+                    expand_pinch=False):
+        """
+        Precon: There is a self.shape
+        """
+        #FIXME remove the precon.  Internally check
+        return self._boundary2mesh(raw_boundary=raw_boundary,
+                    remove_holes=remove_holes,
+                    smooth_indents=smooth_indents,
+                    expand_pinch=expand_pinch)
+    
+        
+    
+    def autoSegment(self, alpha = None,
+                    raw_boundary=True,
+                    remove_holes=False,
+                    smooth_indents=False,
+                    expand_pinch=False): 
+        """
+        Precon: There must be 3 or more vertices in the userVertices structure
+        """
+        self._createBoundary(alpha=alpha)
+        return self._boundary2mesh(raw_boundary=raw_boundary,
+                    remove_holes=remove_holes,
+                    smooth_indents=smooth_indents,
+                    expand_pinch=expand_pinch)
+
+    def _createBoundary(self,alpha=None):
+        """
+        """
+        points=[]
+        for vertex in self.getUserVertices():
+            points.append((vertex.x,vertex.y))
+        self.shape = alpha_shape.alpha_shape.Alpha_Shape(points, alpha = alpha)
+
+
+    def _boundary2mesh(self, raw_boundary=True,
+                    remove_holes=False,
+                    smooth_indents=False,
+                    expand_pinch=False):
+        """
+        Precon there must be a shape object.
+        """
+        self.shape.set_boundary_type(raw_boundary=raw_boundary,
+                                 remove_holes=remove_holes,
+                                 smooth_indents=smooth_indents,
+                                 expand_pinch=expand_pinch)
+        boundary_segs = self.shape.get_boundary()
+        #print "boundary_segs",boundary_segs
+        segs2delete = self.alphaUserSegments
+        #FIXME(DSG-DSG) this algorithm needs comments
+        #FIXME(DSG-DSG) can it be sped up?  It's slow
+        new_segs = {}
+        #alpha_segs = []
+        #user_segs = []
+        for seg in boundary_segs:
+            v1 = self.userVertices[int(seg[0])]
+            v2 = self.userVertices[int(seg[1])]
+            boundary_seg = Segment(v1, v2)
+            new_segs[(v1,v2)] = boundary_seg
+
+        for user_seg in self.userSegments:
+            if new_segs.has_key((user_seg.vertices[0],
+                                user_seg.vertices[1])):
+                del new_segs[user_seg.vertices[0],
+                                user_seg.vertices[1]]
+            elif new_segs.has_key((user_seg.vertices[1],
+                                user_seg.vertices[0])):
+                del new_segs[user_seg.vertices[1],
+                                user_seg.vertices[0]]
+                
+        optimum_alpha = self.shape.get_alpha()
+        alpha_segs_no_user_segs  = new_segs.values()
+        self.alphaUserSegments = alpha_segs_no_user_segs
+        return alpha_segs_no_user_segs, segs2delete, optimum_alpha
+    
+    def _boundary2mesh_old(self, raw_boundary=True,
+                    remove_holes=False,
+                    smooth_indents=False,
+                    expand_pinch=False):
+        """
+        Precon there must be a shape object.
+        """
+        self.shape.set_boundary_type(raw_boundary=raw_boundary,
+                                 remove_holes=remove_holes,
+                                 smooth_indents=smooth_indents,
+                                 expand_pinch=expand_pinch)
+        boundary_segs = self.shape.get_boundary()
+        #print "boundary_segs",boundary_segs
+        segs2delete = self.alphaUserSegments
+
+        #FIXME(DSG-DSG) this algorithm needs comments
+        #FIXME(DSG-DSG) can it be sped up?  It's slow
+        new_segs = []
+        alpha_segs = []
+        user_segs = []
+        for seg in boundary_segs:
+            v1 = self.userVertices[int(seg[0])]
+            v2 = self.userVertices[int(seg[1])]
+            alpha_seg = self.representedAlphaUserSegment(v1, v2)
+            user_seg = self.representedUserSegment(v1, v2)
+            #DSG!!!
+            assert not(not (alpha_seg == None) and not (user_seg == None))
+            if not alpha_seg == None:
+                alpha_segs.append(alpha_seg)
+            elif not user_seg  == None:
+                user_segs.append(user_seg)
+            else:
+                unique_seg = Segment(v1, v2)
+                new_segs.append(unique_seg)
+                
+            for seg in alpha_segs:
+                try:
+                    segs2delete.remove(seg)
+                except:
+                    pass
+        
+        self.alphaUserSegments = []
+        self.alphaUserSegments.extend(new_segs)
+        self.alphaUserSegments.extend(alpha_segs)
+
+        optimum_alpha = self.shape.get_alpha()
+        # need to draw newsegs
+        return new_segs, segs2delete, optimum_alpha
+    
+    def representedAlphaUserSegment(self, v1,v2):
+        identicalSegs= [x for x in self.alphaUserSegments if (x.vertices[0] == v1 and x.vertices[1] == v2) or (x.vertices[0] == v2 and x.vertices[1] == v1) ]
+
+        if identicalSegs == []:
+            return None
+        else:
+            # Only return the first one.
+            return identicalSegs[0]
+    
+    def representedUserSegment(self, v1,v2):
+        identicalSegs= [x for x in self.userSegments if (x.vertices[0] == v1 and x.vertices[1] == v2) or (x.vertices[0] == v2 and x.vertices[1] == v1) ]
+
+        if identicalSegs == []:
+            return None
+        else:
+            # Only return the first one.
+            return identicalSegs[0]
+        
+    def joinVertices(self):
+        """
+        Return list of segments connecting all userVertices
+        in the order they were given
+        
+        Precon: There must be 3 or more vertices in the userVertices structure
+        """
+
+        newsegs = []
+        
+        v1 = self.userVertices[0]
+        for v2 in self.userVertices[1:]:
+            if self.isUserSegmentNew(v1,v2):            
+                newseg = Segment(v1, v2)
+                newsegs.append(newseg)
+            v1 = v2
+
+        #Connect last point to the first
+        v2 = self.userVertices[0]        
+        if self.isUserSegmentNew(v1,v2):            
+                newseg = Segment(v1, v2)
+                newsegs.append(newseg)
+            
+
+        #Update list of user segments        
+        #DSG!!!
+        self.userSegments.extend(newsegs)                
+        return newsegs
+    
+    def normaliseMesh(self,scale, offset, height_scale):
+        [xmin, ymin, xmax, ymax] = self.boxsize()
+        [attmin0, attmax0] = self.maxMinVertAtt(0)
+        #print "[attmin0, attmax0]" ,[attmin0, attmax0] 
+        [attmin1, attmax1] = self.maxMinVertAtt(1)
+        #print [xmin, ymin, xmax, ymax]
+        xrange = xmax - xmin
+        yrange = ymax - ymin
+        if xrange > yrange:
+            min,max = xmin, xmax
+        else:
+            min,max = ymin, ymax
+            
+        for obj in self.getUserVertices():
+            obj.x = (obj.x - xmin)/(max- min)*scale + offset
+            obj.y = (obj.y - ymin)/(max- min)*scale + offset
+            if len(obj.attributes)  > 0 and attmin0 != attmax0:
+                obj.attributes[0] = (obj.attributes[0]-attmin0)/ \
+                                    (attmax0-attmin0)*height_scale
+            if len(obj.attributes) > 1 and attmin1 != attmax1:
+                obj.attributes[1] = (obj.attributes[1]-attmin1)/ \
+                                    (attmax1-attmin1)*height_scale
+            
+        for obj in self.getMeshVertices():
+            obj.x = (obj.x - xmin)/(max- min)*scale + offset
+            obj.y = (obj.y - ymin)/(max- min)*scale + offset
+            if len(obj.attributes)  > 0 and attmin0 != attmax0:
+                obj.attributes[0] = (obj.attributes[0]-attmin0)/ \
+                                    (attmax0-attmin0)*height_scale
+            if len(obj.attributes) > 1 and attmin1 != attmax1:
+                obj.attributes[1] = (obj.attributes[1]-attmin1)/ \
+                                    (attmax1-attmin1)*height_scale
+                
+        for obj in self.getHoles():
+            obj.x = (obj.x - xmin)/(max- min)*scale + offset
+            obj.y = (obj.y - ymin)/(max- min)*scale + offset
+        for obj in self.getRegions():
+            obj.x = (obj.x - xmin)/(max- min)*scale + offset
+            obj.y = (obj.y - ymin)/(max- min)*scale + offset
+        [xmin, ymin, xmax, ymax] = self.boxsize()
+        #print [xmin, ymin, xmax, ymax]
+     
+    def boxsizeVerts(self):
+        """
+        Returns a list of verts denoting a box or triangle that contains verts on the xmin, ymin, xmax and ymax axis.
+        Structure: list of verts 
+        """
+        # FIXME dsg!!! large is a hack
+        #You want the kinds package, part of Numeric:
+        #In [2]: import kinds
+        
+        #In [3]: kinds.default_float_kind.M
+        #kinds.default_float_kind.MAX         kinds.default_float_kind.MIN
+    #kinds.default_float_kind.MAX_10_EXP  kinds.default_float_kind.MIN_10_EXP
+        #kinds.default_float_kind.MAX_EXP     kinds.default_float_kind.MIN_EXP
+
+        #In [3]: kinds.default_float_kind.MIN
+        #Out[3]: 2.2250738585072014e-308
+
+        large = 1e100
+        xmin= large
+        xmax=-large
+        ymin= large
+        ymax=-large
+        for vertex in self.userVertices:
+            if vertex.x < xmin:
+                xmin = vertex.x
+                xminVert = vertex
+            if vertex.x > xmax:
+                xmax = vertex.x
+                xmaxVert = vertex
+                
+            if vertex.y < ymin:
+                ymin = vertex.y 
+                yminVert = vertex
+            if vertex.y > ymax:
+                ymax = vertex.y
+                ymaxVert = vertex
+        verts, count = self.removeDuplicatedVertices([xminVert,xmaxVert,yminVert,ymaxVert])
+         
+        return verts
+    
+    def boxsize(self):
+        """
+        Returns a list denoting a box that contains the entire structure of vertices
+        Structure: [xmin, ymin, xmax, ymax] 
+        """
+        # FIXME dsg!!! large is a hack
+        #You want the kinds package, part of Numeric:
+        #In [2]: import kinds
+        
+        #In [3]: kinds.default_float_kind.M
+        #kinds.default_float_kind.MAX         kinds.default_float_kind.MIN
+    #kinds.default_float_kind.MAX_10_EXP  kinds.default_fltesting oat_kind.MIN_10_EXP
+        #kinds.default_float_kind.MAX_EXP     kinds.default_float_kind.MIN_EXP
+
+        #In [3]: kinds.default_float_kind.MIN
+        #Out[3]: 2.2250738585072014e-308
+
+        large = 1e100
+        xmin= large
+        xmax=-large
+        ymin= large
+        ymax=-large
+        for vertex in self.userVertices:
+            if vertex.x < xmin:
+                xmin = vertex.x 
+            if vertex.x > xmax:
+                xmax = vertex.x
+                
+            if vertex.y < ymin:
+                ymin = vertex.y 
+            if vertex.y > ymax:
+                ymax = vertex.y
+        return [xmin, ymin, xmax, ymax]
+ 
+    def maxMinVertAtt(self, iatt):
+        """
+        Returns a list denoting a box that contains the entire structure of vertices
+        Structure: [xmin, ymin, xmax, ymax] 
+        """
+        # FIXME dsg!!! large is a hacktesting 
+        #You want the kinds package, part of Numeric:
+        #In [2]: import kinds
+        
+        #In [3]: kinds.default_float_kind.M
+        #kinds.default_float_kind.MAX         kinds.default_float_kind.MIN
+    #kinds.default_float_kind.MAX_10_EXP  kinds.default_float_kind.MIN_10_EXP
+        #kinds.default_float_kind.MAX_EXP     kinds.default_float_kind.MIN_EXP
+
+        #In [3]: kinds.default_float_kind.MIN
+        #Out[3]: 2.2250738585072014e-308
+
+        large = 1e100
+        min= large
+        max=-large
+        for vertex in self.userVertices:
+            if len(vertex.attributes) > iatt:
+                if vertex.attributes[iatt] < min:
+                    min = vertex.attributes[iatt]
+                if vertex.attributes[iatt] > max:
+                    max = vertex.attributes[iatt] 
+        for vertex in self.meshVertices:
+            if len(vertex.attributes) > iatt:
+                if vertex.attributes[iatt] < min:
+                    min = vertex.attributes[iatt]
+                if vertex.attributes[iatt] > max:
+                    max = vertex.attributes[iatt] 
+        return [min, max]
+    
+    def scaleoffset(self, WIDTH, HEIGHT):
+        """
+        Returns a list denoting the scale and offset terms that need to be
+        applied when converting  mesh co-ordinates onto grid co-ordinates 
+        Structure: [scale, xoffset, yoffset] 
+        """   
+        OFFSET = 0.05*min([WIDTH, HEIGHT])
+        [xmin, ymin, xmax, ymax] = self.boxsize()
+        SCALE = min([0.9*WIDTH, 0.9*HEIGHT])/max([xmax-xmin, ymax-ymin])
+        
+        if SCALE*xmin < OFFSET:
+            xoffset = abs(SCALE*xmin) + OFFSET
+        if SCALE*xmax > WIDTH - OFFSET:
+            xoffset= -(SCALE*xmax - WIDTH + OFFSET)
+        if SCALE*ymin < OFFSET:
+            b = abs(SCALE*ymin)+OFFSET
+        if SCALE*ymax > HEIGHT-OFFSET:
+            b = -(SCALE*ymax - HEIGHT + OFFSET)
+        yoffset = HEIGHT - b
+        return [SCALE, xoffset, yoffset]
+            
+    def plotMeshTriangle(self,tag = 0,WIDTH = 400,HEIGHT = 400):
+        """
+        Plots all node connections.
+        tag = 0 (no node numbers), tag = 1 (node numbers)
+        """
+
+        try:
+            from Tkinter import Tk, Frame, Button, Canvas, BOTTOM, Label
+
+            [SCALE, xoffset, yoffset] = self.scaleoffset( WIDTH, HEIGHT)
+            
+            root = Tk()
+            frame = Frame(root)
+            frame.pack()
+            button = Button(frame, text="OK", fg="red", command=frame.quit)
+            button.pack(side=BOTTOM)
+            canvas = Canvas(frame,bg="white", width=WIDTH, height=HEIGHT)
+            canvas.pack()
+            text = Label(frame, width=20, height=10, text='triangle mesh')
+            text.pack()
+
+            #print self.meshTriangles
+            for triangle in self.meshTriangles:
+                triangle.draw(canvas,1,
+                              scale = SCALE,
+                              xoffset = xoffset,
+                              yoffset = yoffset )
+                
+            root.mainloop()
+
+        except:
+            print "Unexpected error:", sys.exc_info()[0]
+            raise
+
+            #print """
+            #node::plot Failed.
+            #Most probably, the Tkinter module is not available.
+            #"""
+
+    def plotUserSegments(self,tag = 0,WIDTH = 400,HEIGHT = 400):
+        """
+        Plots all node connections.
+        tag = 0 (no node numbers), tag = 1 (node numbers)
+        """
+
+        try:
+            from Tkinter import Tk, Frame, Button, Canvas, BOTTOM, Label
+            
+            [SCALE, xoffset, yoffset] = self.scaleoffset( WIDTH, HEIGHT)
+
+            root = Tk()
+            frame = Frame(root)
+            frame.pack()
+            button = Button(frame, text="OK", fg="red", command=frame.quit)
+            button.pack(side=BOTTOM)
+            canvas = Canvas(frame, bg="white", width=WIDTH, height=HEIGHT)
+            canvas.pack()
+            text = Label(frame, width=20, height=10, text='user segments')
+            text.pack()
+           
+            for segment in self.getUserSegments():
+                segment.draw(canvas,SCALE, xoffset, yoffset )
+                
+            root.mainloop()
+
+        except:
+            print "Unexpected error:", sys.exc_info()[0]
+            raise
+
+            #print """
+            #node::plot Failed.
+            #Most probably, the Tkinter module is not available.
+            #"""
+            
+
+    def exportASCIIobj(self,ofile):
+        """
+        export a file, ofile, with the format
+         lines:  v <x> <y> <first attribute>
+        f <vertex #>  <vertex #> <vertex #> (of the triangles)
+        """
+        fd = open(ofile,'w')
+        self.writeASCIIobj(fd)   
+        fd.close()
+
+
+    def writeASCIIobj(self,fd):
+        fd.write(" # Triangulation as an obj file\n")
+        numVert = str(len(self.meshVertices))
+        
+        index1 = 1 
+        for vert in self.meshVertices:
+            vert.index1 = index1
+            index1 += 1
+            
+            fd.write("v "
+                     + str(vert.x) + " "
+                     + str(vert.y) + " "
+                     + str(vert.attributes[0]) + "\n")
+            
+        for tri in self.meshTriangles:
+            fd.write("f "
+                     + str(tri.vertices[0].index1) + " " 
+                     + str(tri.vertices[1].index1) + " " 
+                     + str(tri.vertices[2].index1) + "\n")
+           
+    def exportASCIIsegmentoutlinefile(self,ofile):
+        """Write the boundary user mesh info, eg
+         vertices that are connected to segments,
+         segments
+        """
+        
+        verts = {}
+        for seg in self.getUserSegments():
+            verts[seg.vertices[0]] = seg.vertices[0]
+            verts[seg.vertices[1]] = seg.vertices[1]
+        #print "verts.values()",verts.values() 
+        meshDict = self.Mesh2IOOutlineDict(userVertices=verts.values())
+        load_mesh.loadASCII.export_mesh_file(ofile,meshDict)
+        
+        # exportASCIImeshfile   - this function is used
+    def export_mesh_file(self,ofile):
+        """
+        export a file, ofile, with the format
+        """
+        
+        dict = self.Mesh2IODict()
+        load_mesh.loadASCII.export_mesh_file(ofile,dict)
+
+    def exportPointsFile(self,ofile):
+        """
+        export a points (.xya or .pts)  file, ofile.
+        
+        """
+        
+        mesh_dict = self.Mesh2IODict()
+        point_dict = {}
+        point_dict['attributelist'] = {} #this will need to be expanded..
+                                         # if attributes are brought back in. 
+        point_dict['geo_reference'] = self.geo_reference
+        if mesh_dict['vertices'] == []:
+            point_dict['pointlist'] = mesh_dict['points']
+        else:
+            point_dict['pointlist'] = mesh_dict['vertices']
+
+        load_mesh.loadASCII.export_points_file(ofile,point_dict)
+
+        
+########### IO CONVERTERS ##################
+        """
+        The dict fromat for IO with .tsh files is;
+        (the triangulation)
+        vertices: [[x1,y1],[x2,y2],...] (lists of doubles)
+        vertex_attributes: [[a11,a12,...],[a21,a22],...] (lists of doubles)
+        vertex_attribute_titles:[A1Title, A2Title ...] (A list of strings)
+        segments: [[v1,v2],[v3,v4],...] (lists of integers) 
+        segment_tags : [tag,tag,...] list of strings
+        triangles : [(v1,v2,v3), (v4,v5,v6),....] lists of points
+        triangle tags: [s1,s2,...] A list of strings
+        triangle neighbors: [[t1,t2,t3], [t4,t5,t6],..] lists of triangles
+        
+        (the outline)   
+        points: [[x1,y1],[x2,y2],...] (lists of doubles)
+        point_attributes: [[a11,a12,...],[a21,a22],...] (lists of doubles)
+        outline_segments: [[point1,point2],[p3,p4],...] (lists of integers) 
+        outline_segment_tags : [tag1,tag2,...] list of strings
+        holes : [[x1,y1],...](List of doubles, one inside each hole region)
+        regions : [ [x1,y1],...] (List of 4 doubles)
+        region_tags : [tag1,tag2,...] (list of strings)
+        region_max_areas: [ma1,ma2,...] (A list of doubles)
+        {Convension: A -ve max area means no max area}
+        
+        """
+     
+
+                               
+    def Mesh2IODict(self):
+        """
+        Convert the triangulation and outline info of a mesh to a dictionary
+        structure
+        """
+        dict = self.Mesh2IOTriangulationDict()
+        dict_mesh = self.Mesh2IOOutlineDict()
+        for element in dict_mesh.keys():
+            dict[element] = dict_mesh[element]
+
+        # add the geo reference
+        dict['geo_reference'] = self.geo_reference
+        return dict
+    
+    def Mesh2IOTriangulationDict(self):
+        """
+        Convert the Mesh to a dictionary of lists describing the
+        triangulation variables;
+        
+        Used to produce .tsh file
+        """
+        
+        meshDict = {}        
+        vertices=[]
+        vertex_attributes=[]
+
+        self.maxVertexIndex=0
+        for vertex in self.meshVertices:
+            vertex.index = self.maxVertexIndex 
+            vertices.append([vertex.x,vertex.y])
+            vertex_attributes.append(vertex.attributes)           
+            self.maxVertexIndex += 1
+
+        meshDict['vertices'] = vertices
+        meshDict['vertex_attributes'] = vertex_attributes
+        meshDict['vertex_attribute_titles'] = self.attributeTitles
+        #segments
+        segments=[]
+        segment_tags=[]
+        for seg in self.meshSegments:
+            segments.append([seg.vertices[0].index,seg.vertices[1].index])
+            segment_tags.append(seg.tag)
+        meshDict['segments'] =segments 
+        meshDict['segment_tags'] =segment_tags
+
+        # Make sure that the indexation is correct
+        index = 0
+        for tri in self.meshTriangles:
+            tri.index = index           
+            index += 1
+
+        triangles = []
+        triangle_tags = []
+        triangle_neighbors = []
+        for tri in self.meshTriangles:  
+            triangles.append([tri.vertices[0].index,tri.vertices[1].index,tri.vertices[2].index])  
+            triangle_tags.append(tri.attribute)
+            neighborlist = [-1,-1,-1]
+            for neighbor,index in map(None,tri.neighbors,
+                                      range(len(tri.neighbors))):
+                if neighbor:    
+                    neighborlist[index] = neighbor.index
+            triangle_neighbors.append(neighborlist)
+        
+        meshDict['triangles'] = triangles
+        meshDict['triangle_tags'] = triangle_tags
+        meshDict['triangle_neighbors'] = triangle_neighbors
+        
+        #print "mesh.Mesh2IOTriangulationDict*)*)"
+        #print meshDict
+        #print "mesh.Mesh2IOTriangulationDict*)*)"
+
+        return meshDict
+
+                                                     
+    def Mesh2IOOutlineDict(self, userVertices=None,
+                        userSegments=None,
+                        holes=None,
+                        regions=None):
+        """
+        Convert the mesh outline to a dictionary of the lists needed for the
+        triang module;
+        
+        Note, this adds an index attribute to the user Vertex objects.
+
+        Used to produce .tsh file and output to triangle
+        """
+        if userVertices is None:
+            userVertices = self.getUserVertices()
+        if userSegments is None:
+            userSegments = self.getUserSegments()
+        if holes is None:
+            holes = self.getHoles()
+        if regions is None:
+            regions = self.getRegions()
+            
+        meshDict = {}
+        #print "userVertices",userVertices
+        #print "userSegments",userSegments 
+        pointlist=[]
+        pointattributelist=[]
+        index = 0
+        for vertex in userVertices:
+            vertex.index = index 
+            pointlist.append([vertex.x,vertex.y])
+            pointattributelist.append(vertex.attributes)
+            
+            index += 1
+        meshDict['points'] = pointlist
+        meshDict['point_attributes'] = pointattributelist
+
+        segmentlist=[]
+        segmenttaglist=[]
+        for seg in userSegments:
+            segmentlist.append([seg.vertices[0].index,seg.vertices[1].index])
+            segmenttaglist.append(seg.tag)
+        meshDict['outline_segments'] =segmentlist 
+        meshDict['outline_segment_tags'] =segmenttaglist
+        
+        holelist=[]
+        for hole in holes:
+            holelist.append([hole.x,hole.y]) 
+        meshDict['holes'] = holelist
+        
+        regionlist=[]
+        regiontaglist = []
+        regionmaxarealist = []
+        for region in regions:
+            regionlist.append([region.x,region.y])
+            regiontaglist.append(region.getTag())
+            
+            if (region.getMaxArea() != None): 
+                regionmaxarealist.append(region.getMaxArea())
+            else:
+                regionmaxarealist.append( load_mesh.loadASCII.NOMAXAREA)
+        meshDict['regions'] = regionlist
+        meshDict['region_tags'] = regiontaglist
+        meshDict['region_max_areas'] = regionmaxarealist
+        #print "*(*("
+        #print meshDict
+        #print meshDict['regionlist']
+        #print "*(*("
+        return meshDict
+
+    def IOTriangulation2Mesh(self, genDict):
+        """
+        Set the mesh attributes given an tsh IO dictionary
+        """
+        #Clear the current generated mesh values
+        self.meshTriangles=[]
+        self.attributeTitles=[]
+        self.meshSegments=[]
+        self.meshVertices=[]
+
+        #print "mesh.setTriangulation@#@#@#"
+        #print genDict
+        #print "@#@#@#"
+
+        self.maxVertexIndex = 0
+        for point in genDict['vertices']:
+            v=Vertex(point[0], point[1])
+            v.index =  self.maxVertexIndex
+            self.maxVertexIndex +=1
+            self.meshVertices.append(v)
+
+        self.attributeTitles = genDict['vertex_attribute_titles']
+
+        index = 0
+        for seg,tag in map(None,genDict['segments'],genDict['segment_tags']):
+            segObject = Segment( self.meshVertices[seg[0]],
+                           self.meshVertices[seg[1]], tag = tag )
+            segObject.index = index
+            index +=1
+            self.meshSegments.append(segObject)
+
+        index = 0
+        for triangle in genDict['triangles']:
+            tObject =Triangle( self.meshVertices[triangle[0]],
+                        self.meshVertices[triangle[1]],
+                        self.meshVertices[triangle[2]] )
+            tObject.index = index
+            index +=1
+            self.meshTriangles.append(tObject)
+
+        index = 0
+        for att in genDict['triangle_tags']:
+            if att == []:
+                self.meshTriangles[index].setAttribute("")
+            else:
+                self.meshTriangles[index].setAttribute(att)
+            index += 1
+            
+        index = 0
+        for att in genDict['vertex_attributes']:
+            if att == None:
+                self.meshVertices[index].setAttributes([])
+            else:
+                self.meshVertices[index].setAttributes(att)
+            index += 1
+    
+        index = 0
+        for triangle in genDict['triangle_neighbors']:
+            # Build a list of triangle object neighbors
+            ObjectNeighbor = []
+            for neighbor in triangle:
+                if ( neighbor != -1):
+                    ObjectNeighbor.append(self.meshTriangles[neighbor])
+                else:
+                    ObjectNeighbor.append(None)
+            self.meshTriangles[index].setNeighbors(ObjectNeighbor[0],ObjectNeighbor[1],ObjectNeighbor[2])
+            index += 1
+
+
+    def IOOutline2Mesh(self, genDict):
+        """
+        Set the outline (user Mesh attributes) given a IO tsh dictionary
+        
+        mesh is an instance of a mesh object
+        """
+        #Clear the current user mesh values
+        self.clearUserSegments()
+        self.userVertices=[]
+        self.Holes=[]
+        self.Regions=[]
+
+        #print "mesh.IOOutline2Mesh@#@#@#"
+        #print "genDict",genDict
+        #print "@#@#@#"
+        
+        #index = 0
+        for point in genDict['points']:
+            v=Vertex(point[0], point[1])
+            #v.index = index
+            #index +=1
+            self.userVertices.append(v)
+
+        #index = 0
+        for seg,tag in map(None,genDict['outline_segments'],genDict['outline_segment_tags']):
+            segObject = Segment( self.userVertices[seg[0]],
+                           self.userVertices[seg[1]], tag = tag )
+            #segObject.index = index
+            #index +=1
+            self.userSegments.append(segObject)
+
+# Remove the loading of attribute info.
+# Have attribute info added using least_squares in pyvolution
+#         index = 0
+#         for att in genDict['point_attributes']:
+#             if att == None:
+#                 self.userVertices[index].setAttributes([])
+#             else:
+#                 self.userVertices[index].setAttributes(att)
+#            index += 1
+        
+        #index = 0
+        for point in genDict['holes']:
+            h=Hole(point[0], point[1])
+            #h.index = index
+            #index +=1
+            self.holes.append(h)
+
+        #index = 0
+        for reg,att,maxArea in map(None,
+                                   genDict['regions'],
+                                   genDict['region_tags'],
+                                   genDict['region_max_areas']):
+            if maxArea > 0:  # maybe I should ref NOMAXAREA? Prob' not though
+                Object = Region( reg[0],
+                                 reg[1],
+                                 tag = att,
+                                 maxArea = maxArea)
+            else:
+                Object = Region( reg[0],
+                                 reg[1],
+                                 tag = att)
+                
+            #Object.index = index
+            #index +=1
+            self.regions.append(Object)
+  
+############################################
+
+        
+    def refineSet(self,setName):
+        Triangles = self.sets[self.setID[setName]]
+        Refine(self,Triangles)
+
+    def selectAllTriangles(self):
+        A=[]
+        A.extend(self.meshTriangles)
+        if not('All' in self.setID.keys()):
+            self.setID['All']=len(self.sets)
+            self.sets.append(A)
+        else:
+            self.sets[self.setID['All']]=A
+        return 'All'
+        # and objectIDs
+
+
+    def clearSelection(self):
+        A = []
+        if not('None' in self.setID.keys()):
+            self.setID['None']=len(self.sets)
+            self.sets.append(A)
+        return 'None'
+
+    def drawSet(self,canvas,setName,SCALE,colour=SET_COLOUR):
+    #FIXME Draws over previous triangles - may bloat canvas
+        Triangles = self.sets[self.setID[setName]]
+        for triangle in Triangles:
+            triangle.draw(canvas,1,
+                          scale = SCALE,
+                          colour = colour)
+	    
+    def undrawSet(self,canvas,setName,SCALE,colour='green'):
+    #FIXME Draws over previous lines - may bloat canvas
+        Triangles = self.sets[self.setID[setName]]
+        for triangle in Triangles:
+            triangle.draw(canvas,1,
+                          scale = SCALE,
+                          colour = colour)
+
+    def weed(self,Vertices,Segments):
+        #Depreciated
+        #weed out existing duplicates
+        print 'len(self.getUserSegments())'
+        print len(self.getUserSegments())
+        print 'len(self.getUserVertices())'
+        print len(self.getUserVertices())
+
+        point_keys = {}
+        for vertex in Vertices:
+            point = (vertex.x,vertex.y)
+            point_keys[point]=vertex
+        #inlined would looks very ugly
+
+        line_keys = {}
+        for segment in Segments:
+            vertex1 = segment.vertices[0]
+            vertex2 = segment.vertices[1]
+            point1 = (vertex1.x,vertex1.y)
+            point2 = (vertex2.x,vertex2.y)
+            segment.vertices[0]=point_keys[point1]
+            segment.vertices[1]=point_keys[point2]
+            vertex1 = segment.vertices[0]
+            vertex2 = segment.vertices[1]
+            point1 = (vertex1.x,vertex1.y)
+            point2 = (vertex2.x,vertex2.y)
+            line1 = (point1,point2)
+            line2 = (point2,point1)
+            if not (line_keys.has_key(line1) \
+                 or line_keys.has_key(line2)):
+                 line_keys[line1]=segment
+        Vertices=point_keys.values()
+        Segments=line_keys.values()
+        return Vertices,Segments
+
+    def segs_to_dict(self,segments):
+        dict={}
+        for segment in segments:
+            vertex1 = segment.vertices[0]
+            vertex2 = segment.vertices[1]
+            point1 = (vertex1.x,vertex1.y)
+            point2 = (vertex2.x,vertex2.y)
+            line = (point1,point2)
+            dict[line]=segment
+        return dict
+
+    def seg2line(self,s):
+        return ((s.vertices[0].x,s.vertices[0].y,)\
+                (s.vertices[1].x,s.vertices[1].y))
+
+    def line2seg(self,line,tag=None):
+        point0 = self.point2ver(line[0])
+        point1 = self.point2ver(line[1])
+        return Segment(point0,point1,tag=tag)
+
+    def ver2point(self,vertex):
+        return (vertex.x,vertex.y)
+
+    def point2ver(self,point):
+        return Vertex(point[0],point[1])
+
+    def smooth_polySet(self,min_radius=0.05):
+        #for all pairs of connecting segments:
+        #    propose a new segment that replaces the 2
+
+        #    If the difference between the new segment
+        #    and the old lines is small: replace the
+        #    old lines.
+
+        seg2line = self.seg2line
+        ver2point= self.ver2point
+        line2seg = self.line2seg
+        point2ver= self.point2ver
+
+        #create dictionaries of lines -> segments
+        userSegments = self.segs_to_dict(self.userSegments)
+        alphaSegments = self.segs_to_dict(self.alphaUserSegments)
+
+        #lump user and alpha segments
+        for key in alphaSegments.keys():
+            userSegments[key]=alphaSegments[key]
+
+        #point_keys = tuple -> vertex
+        #userVertices = vertex -> [line,line] - lines from that node
+        point_keys = {}
+        userVertices={}
+        for vertex in self.getUserVertices():
+            point = ver2point(vertex)
+            if not point_keys.has_key(point):
+                point_keys[point]=vertex
+                userVertices[vertex]=[]
+        for key in userSegments.keys():
+            line = key
+            point_0 = key[0]
+            point_1 = key[1]
+            userVertices[point_keys[point_0]].append(line)
+            userVertices[point_keys[point_1]].append(line)
+
+        for point in point_keys.keys():
+            try:
+            #removed keys can cause keyerrors
+                vertex = point_keys[point]
+		lines = userVertices[vertex]
+    
+                #if there are 2 lines on the node
+		if len(lines)==2:
+		    line_0 = lines[0]
+		    line_1 = lines[1]
+    
+                    #if the tags are the the same on the 2 lines
+		    if userSegments[line_0].tag == userSegments[line_1].tag:
+			tag = userSegments[line_0].tag 
+    
+                        #point_a is one of the next nodes, point_b is the other
+			if point==line_0[0]:
+			    point_a = line_0[1]
+			if point==line_0[1]:
+			    point_a = line_0[0]
+			if point==line_1[0]:
+			    point_b = line_1[1]
+			if point==line_1[1]:
+			    point_b = line_1[0]
+    
+    
+                        #line_2 is proposed
+			line_2 = (point_a,point_b)
+
+			#calculate the area of the triangle between
+			#the two existing segments and the proposed
+			#new segment
+			ax = point_a[0]
+			ay = point_a[1]
+			bx = point_b[0]
+			by = point_b[1]
+			cx = point[0]
+			cy = point[1]
+			area=abs((bx*ay-ax*by)+(cx*by-bx*cy)+(ax*cy-cx*ay))/2
+
+			#calculate the perimeter
+			len_a =  ((cx-bx)**2+(cy-by)**2)**0.5 
+			len_b =  ((ax-cx)**2+(ay-cy)**2)**0.5 
+			len_c =  ((bx-ax)**2+(by-ay)**2)**0.5 
+			perimeter = len_a+len_b+len_c
+
+			#calculate the radius
+			r = area/(2*perimeter)
+
+			#if the radius is small: then replace the existing
+			#segments with the new one
+			if r < min_radius:
+			    if len_c < min_radius: append = False
+			    else: append = True
+			    #if the new seg is also time, don't add it
+			    if append:
+				segment = self.line2seg(line_2,tag=tag)
+
+			    list_a=userVertices[point_keys[point_a]]
+			    list_b=userVertices[point_keys[point_b]]
+
+			    if line_0 in list_a:
+				list_a.remove(line_0)
+			    else:
+				list_a.remove(line_1)
+
+			    if line_0 in list_b:
+				list_b.remove(line_0)
+			    else:
+				list_b.remove(line_1)
+
+			    if append:
+				list_a.append(line_2)
+				list_b.append(line_2)
+			    else:
+				if len(list_a)==0:
+				    userVertices.pop(point_keys[point_a])
+				    point_keys.pop(point_a)
+				if len(list_b)==0:
+				    userVertices.pop(point_keys[point_b])
+				    point_keys.pop(point_b)
+
+			    userVertices.pop(point_keys[point])
+			    point_keys.pop(point)
+			    userSegments.pop(line_0)
+			    userSegments.pop(line_1)
+
+			    if append:
+				userSegments[line_2]=segment
+            except:
+                pass
+
+        #self.userVerticies = userVertices.keys()
+        #self.userSegments = []
+        #for key in userSegments.keys():
+        #    self.userSegments.append(userSegments[key])
+        #self.alphaUserSegments = []
+
+        self.userVerticies = []
+        self.userSegments = []
+        self.alphaUserSegments = []
+
+        return userVertices,userSegments,alphaSegments
+
+    def triangles_to_polySet(self,setName):
+        #self.smooth_polySet()
+
+        seg2line = self.seg2line
+        ver2point= self.ver2point
+        line2seg = self.line2seg
+        point2ver= self.point2ver
+
+        from Numeric import array,allclose
+        #turn the triangles into a set
+        Triangles = self.sets[self.setID[setName]]
+        Triangles_dict = {}
+        for triangle in Triangles:
+            Triangles_dict[triangle]=None
+ 
+
+        #create a dict of points to vertexes (tuple -> object)
+        #also create a set of vertexes (object -> True)
+        point_keys = {}
+        userVertices={}
+        for vertex in self.getUserVertices():
+            point = ver2point(vertex)
+            if not point_keys.has_key(point):
+                point_keys[point]=vertex
+                userVertices[vertex]=True
+
+        #create a dict of lines to segments (tuple -> object)
+        userSegments = self.segs_to_dict(self.userSegments)
+        #append the userlines in an affine linespace
+        affine_lines = Affine_Linespace()
+        for line in userSegments.keys():
+            affine_lines.append(line)
+        alphaSegments = self.segs_to_dict(self.alphaUserSegments)
+        for line in alphaSegments.keys():
+            affine_lines.append(line)
+        
+        for triangle in Triangles:
+            for i in (0,1,2):
+                #for every triangles neighbour:
+                if not Triangles_dict.has_key(triangle.neighbors[i]):
+                #if the neighbour is not in the set:
+                    a = triangle.vertices[i-1]
+                    b = triangle.vertices[i-2]
+                    #Get possible matches:
+                    point_a = ver2point(a)
+                    point_b = ver2point(b)
+                    midpoint = ((a.x+b.x)/2,(a.y+b.y)/2)
+                    line = (point_a,point_b)
+                    tag = None
+
+
+                    #this bit checks for matching lines
+                    possible_lines = affine_lines[line] 
+                    possible_lines = unique(possible_lines)
+                    found = 0                            
+                    for user_line in possible_lines:
+                        if self.point_on_line(midpoint,user_line):
+                            found+=1
+                            assert found<2
+                            if userSegments.has_key(user_line):
+                                parent_segment = userSegments.pop(user_line)
+                            if alphaSegments.has_key(user_line):
+                                parent_segment = alphaSegments.pop(user_line)
+                            tag = parent_segment.tag
+                            offspring = [line]
+                            offspring.extend(self.subtract_line(user_line,line))
+                            affine_lines.remove(user_line)
+                            for newline in offspring:
+                                line_vertices = []
+                                for point in newline:
+                                    if point_keys.has_key(point):
+                                        vert = point_keys[point]
+                                    else:
+                                        vert = Vertex(point[0],point[1])
+                                        userVertices[vert]=True
+                                        point_keys[point]=vert
+                                    line_vertices.append(vert)
+                                segment = Segment(line_vertices[0],line_vertices[1],tag)
+                                userSegments[newline]=segment
+                                affine_lines.append(newline)
+                            #break
+                    assert found<2
+
+
+
+                    #if no matching lines
+                    if not found:
+                        line_vertices = []
+		        for point in line:
+			    if point_keys.has_key(point):
+			        vert = point_keys[point]
+			    else:
+				vert = Vertex(point[0],point[1])
+				userVertices[vert]=True
+				point_keys[point]=vert
+			    line_vertices.append(vert)
+			segment = Segment(line_vertices[0],line_vertices[1],tag)
+                        userSegments[line]=segment
+                        affine_lines.append(line)
+        
+        self.userVerticies = []
+        self.userSegments = []
+        self.alphaUserSegments = []
+
+        return userVertices,userSegments,alphaSegments
+
+    def subtract_line(self,parent,child):
+        #Subtracts child from parent
+        #Requires that the child is a 
+        #subline of parent to work.
+
+        from Numeric import allclose,dot,array
+        A= parent[0]
+        B= parent[1]
+        a = child[0]
+        b = child[1]
+
+        A_array = array(parent[0])
+        B_array = array(parent[1])
+        a_array   = array(child[0])
+        b_array   = array(child[1])
+
+        assert not A == B
+        assert not a == b
+
+        answer = []
+
+        #if the new line does not share a 
+        #vertex with the old one
+        if not (allclose(A_array,a_array)\
+             or allclose(B_array,b_array)\
+             or allclose(A_array,b_array)\
+             or allclose(a_array,B_array)):
+            if dot(A_array-a_array,A_array-a_array) \
+            < dot(A_array-b_array,A_array-b_array):
+                sibling1 = (A,a)
+                sibling2 = (B,b)
+                return [sibling1,sibling2]
+            else:
+                sibling1 = (A,b)
+                sibling2 = (B,a)
+                return [sibling1,sibling2]
+
+        elif allclose(A_array,a_array):
+            if allclose(B_array,b_array):
+                return []
+	    else:
+                sibling = (b,B)
+                return [sibling]
+    	elif allclose(B_array,b_array):
+            sibling = (a,A)
+            return [sibling]
+
+        elif allclose(A_array,b_array):
+            if allclose(B,a):
+                return []
+	    else:
+                sibling = (a,B)
+                return [sibling]
+        elif allclose(a_array,B_array):
+            sibling = (b,A)
+            return [sibling]
+
+    def point_on_line(self,point,line):
+        #returns true within a tolerance of 3 degrees
+        x=point[0]
+        y=point[1]
+        x0=line[0][0]
+        x1=line[1][0]
+        y0=line[0][1]
+        y1=line[1][1]
+        from Numeric import array, dot, allclose
+        from math import sqrt
+        tol = 3. #DEGREES
+        tol = tol*3.1415/180
+
+        a = array([x - x0, y - y0]) 
+        a_normal = array([a[1], -a[0]])      
+        len_a_normal = sqrt(sum(a_normal**2)) 
+
+        b = array([x1 - x0, y1 - y0])  	       
+        len_b = sqrt(sum(b**2)) 
+    
+        if abs(dot(a_normal, b)/(len_b*len_a_normal))< tol:
+            #Point is somewhere on the infinite extension of the line
+
+            len_a = sqrt(sum(a**2))     	       	                
+            if dot(a, b) >= 0 and len_a <= len_b:
+               return True
+            else:   
+               return False
+        else:
+          return False
+
+    def line_length(self,line):
+        x0=line[0][0]
+        x1=line[1][0]
+        y0=line[0][1]
+        y1=line[1][1]
+        return ((x1-x0)**2-(y1-y0)**2)**0.5     
+
+    def threshold(self,setName,min=None,max=None,attribute_name = 'elevation'):
+        """
+        threshold using  d
+        """
+        triangles = self.sets[self.setID[setName]]
+        A = []
+
+        if attribute_name in self.attributeTitles:
+            i = self.attributeTitles.index(attribute_name)
+        else: i = -1#no attribute
+        if not max == None:
+            for t in triangles:
+                if (min<self.av_att(t,i)<max):
+                    A.append(t)
+        else:
+            for t in triangles:
+                if (min<self.av_att(t,i)):
+                    A.append(t)
+        self.sets[self.setID[setName]] = A
+
+    def general_threshold(self,setName,min=None,max=None\
+              ,attribute_name = 'elevation',function=None):
+        """
+        Thresholds the triangles 
+        """
+        from visual.graph import arange,ghistogram,color as colour
+        triangles = self.sets[self.setID[setName]]
+        A = []
+        data=[]
+        #data is for the graph
+
+        if attribute_name in self.attributeTitles:
+            i = self.attributeTitles.index(attribute_name)
+        else: i = -1
+        if not max == None:
+            for t in triangles:
+                value=function(t,i)
+                if (min<value<max):
+                    A.append(t)
+                data.append(value)
+        else:
+            for t in triangles:
+                value=function(t,i)
+                if (min<value):
+                    A.append(t)
+                data.append(value)
+        self.sets[self.setID[setName]] = A
+
+        if self.visualise_graph:
+            if len(data)>0:
+                max=data[0]
+                min=data[0]
+                for value in data:
+                    if value > max:
+                        max = value
+                    if value < min:
+                        min = value
+
+                inc = (max-min)/100
+
+                histogram = ghistogram(bins=arange(min,max,inc),\
+                             color = colour.red)
+                histogram.plot(data=data)
+        
+    def av_att(self,triangle,i):
+        if i==-1: return 1
+        else:
+            #evaluates the average attribute of the vertices of a triangle.
+            V = triangle.getVertices()
+            a0 = (V[0].attributes[i])
+            a1 = (V[1].attributes[i])
+            a2 = (V[2].attributes[i])
+            return (a0+a1+a2)/3
+
+    def Courant_ratio(self,triangle,index):
+        """
+        Uses the courant threshold
+        """
+        e = self.av_att(triangle,index)
+        A = triangle.calcArea()
+        P = triangle.calcP()
+        r = A/(2*P)
+        e = max(0.1,abs(e))
+        return r/e**0.5
+
+    def Gradient(self,triangle,index):
+        V = triangle.vertices
+        x0, y0, x1, y1, x2, y2, q0, q1, q2 = V[0].x,V[0].y,V[1].x,V[1].y,V[2].x,V[2].y,V[0].attributes[index],V[1].attributes[index],V[2].attributes[index]
+        grad_x,grad_y = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
+        if ((grad_x**2)+(grad_y**2))**(0.5)<0:
+            print ((grad_x**2)+(grad_y**2))**(0.5)
+        return ((grad_x**2)+(grad_y**2))**(0.5)
+    
+
+    def append_triangle(self,triangle):
+        self.meshTriangles.append(triangle)
+
+    def replace_triangle(self,triangle,replacement):
+        i = self.meshTriangles.index(triangle)
+        self.meshTriangles[i]=replacement
+        assert replacement in self.meshTriangles
+
+def importUngenerateFile(ofile):
+    """
+    import a file, ofile, with the format
+    [poly]
+    poly format:
+    First line:  <# of vertices> <x centroid> <y centroid>
+    Following lines: <x> <y> 
+    last line:  "END"
+
+    Note: These are clockwise.
+    """
+    fd = open(ofile,'r')
+    Dict = readUngenerateFile(fd)
+    fd.close()
+    return Dict
+
+def readUngenerateFile(fd):
+    """
+    import a file, ofile, with the format
+    [poly]
+    poly format:
+    First line:  <# of polynomial> <x centroid> <y centroid>
+    Following lines: <x> <y> 
+    last line:  "END"
+    """
+    END_DELIMITER = 'END\n'
+    
+    points = []
+    segments = []
+    
+    isEnd = False
+    line = fd.readline() #not used <# of polynomial> <x> <y>
+    while not isEnd:
+        line = fd.readline()
+        fragments = line.split()
+        vert = [float(fragments.pop(0)),float(fragments.pop(0))]
+        points.append(vert)
+        PreviousVertIndex = len(points)-1
+        firstVertIndex = PreviousVertIndex
+        
+        line = fd.readline() #Read the next line
+        while line <> END_DELIMITER: 
+            #print "line >" + line + "<"
+            fragments = line.split()
+            vert = [float(fragments.pop(0)),float(fragments.pop(0))]
+            points.append(vert)
+            thisVertIndex = len(points)-1
+            segment = [PreviousVertIndex,thisVertIndex]
+            segments.append(segment)
+            PreviousVertIndex = thisVertIndex
+            line = fd.readline() #Read the next line
+            i =+ 1
+        # If the last and first segments are the same,
+        # Remove the last segment and the last vertex
+        # then add a segment from the second last vert to the 1st vert
+        thisVertIndex = len(points)-1
+        firstVert = points[firstVertIndex]
+        thisVert = points[thisVertIndex]
+        #print "firstVert",firstVert
+        #print "thisVert",thisVert
+        if (firstVert[0] == thisVert[0] and firstVert[1] == thisVert[1]):
+            points.pop()
+            segments.pop()
+            thisVertIndex = len(points)-1
+            segments.append([thisVertIndex, firstVertIndex])
+        
+        line = fd.readline() # read <# of polynomial> <x> <y> OR END
+        #print "line >>" + line + "<<"
+        if line == END_DELIMITER:
+            isEnd = True
+    
+    #print "points", points       
+    #print "segments", segments
+    ungenerated_dict = {}
+    ungenerated_dict['points'] = points
+    ungenerated_dict['segments'] = segments
+    return ungenerated_dict
+
+def importMeshFromFile(ofile):
+    """returns a mesh object, made from a .xya/.pts or .tsh/.msh file
+    Often raises IOError,RuntimeError
+    """
+    newmesh = None
+    if (ofile[-4:]== ".xya" or ofile[-4:]== ".pts"):
+        dict = load_mesh.loadASCII.import_points_file(ofile)
+        dict['points'] = dict['pointlist']
+        dict['outline_segments'] = []
+        dict['outline_segment_tags'] = []
+        dict['regions'] = []
+        dict['region_tags'] = []
+        dict['region_max_areas'] = []
+        dict['holes'] = [] 
+        newmesh= Mesh(geo_reference = dict['geo_reference'])
+        newmesh.IOOutline2Mesh(dict)
+        counter = newmesh.removeDuplicatedUserVertices()
+        if (counter >0):
+            print "%i duplicate vertices removed from dataset" % (counter)
+    elif (ofile[-4:]== ".tsh" or ofile[-4:]== ".msh"):
+        dict = load_mesh.loadASCII.import_mesh_file(ofile)
+        #print "********"
+        #print "zq mesh.dict",dict
+        #print "********" 
+        newmesh= Mesh()
+        newmesh.IOOutline2Mesh(dict)
+        newmesh.IOTriangulation2Mesh(dict)
+    else:
+        raise RuntimeError
+    
+    if dict.has_key('geo_reference') and not dict['geo_reference'] == None:
+        newmesh.geo_reference = dict['geo_reference']
+    return newmesh
+
+def loadPickle(currentName):
+    fd = open(currentName)
+    mesh = pickle.load(fd)
+    fd.close()
+    return mesh
+    
+def square_outline(side_length = 1,up = "top", left = "left", right = "right",
+                   down = "bottom", regions = False):
+    
+        a = Vertex (0,0)
+        b = Vertex (0,side_length)
+        c = Vertex (side_length,0)
+        d = Vertex (side_length,side_length)
+      
+        s2 = Segment(b,d, tag = up)
+        s3 = Segment(b,a, tag = left)
+        s4 = Segment(d,c, tag = right)
+        s5 = Segment(a,c, tag = down)
+
+        if regions:
+            e =  Vertex (side_length/2,side_length/2)
+            s6 = Segment(a,e, tag = down + left)
+            s7 = Segment(b,e, tag = up + left)
+            s8 = Segment(c,e, tag = down + right)
+            s9 = Segment(d,e, tag = up + right)
+            r1 = Region(side_length/2,3.*side_length/4, tag = up)
+            r2 = Region(1.*side_length/4,side_length/2, tag = left)
+            r3 = Region(3.*side_length/4,side_length/2, tag = right)
+            r4 = Region(side_length/2,1.*side_length/4, tag = down)
+            mesh = Mesh(userVertices=[a,b,c,d,e],
+                        userSegments=[s2,s3,s4,s5,s6,s7,s8,s9],
+                        regions = [r1,r2,r3,r4])
+        else:
+            mesh = Mesh(userVertices=[a,b,c,d],
+                 userSegments=[s2,s3,s4,s5])
+     
+        return mesh
+
+    
+
+def region_strings2ints(region_list):
+    """Given a list of (x_int,y_int,tag_string) lists it returns a list of
+    (x_int,y_int,tag_int) and a list to convert the tag_int's back to
+    the tag_strings  
+    """
+    # Make sure "" has an index of 0 
+    region_list.reverse()
+    region_list.append((1.0,2.0,""))
+    region_list.reverse()
+    convertint2string = []
+    for i in xrange(len(region_list)):
+        convertint2string.append(region_list[i][2])
+        if len(region_list[i]) == 4: # there's an area value
+            region_list[i] = (region_list[i][0],
+                              region_list[i][1],i,region_list[i][3])
+        elif len(region_list[i]) == 3: # no area value
+            region_list[i] = (region_list[i][0],region_list[i][1],i)
+        else:
+            print "The region list has a bad size"
+            # raise an error ..
+            raise Error
+
+    #remove "" from the region_list
+    region_list.pop(0)
+    
+    return [region_list, convertint2string]
+
+def region_ints2strings(region_list,convertint2string):
+    """Reverses the transformation of region_strings2ints
+    """
+    if region_list[0] != []:
+        for i in xrange(len(region_list)):
+            region_list[i] = [convertint2string[int(region_list[i][0])]]
+    return region_list
+
+def segment_ints2strings(intlist, convertint2string):
+    """Reverses the transformation of segment_strings2ints """
+    stringlist = []
+    for x in intlist:
+        stringlist.append(convertint2string[x])
+    return stringlist
+
+def segment_strings2ints(stringlist, preset):
+    """Given a list of strings return a list of 0 to n ints which represent
+    the strings and a converting list of the strings, indexed by 0 to n ints.
+    Also, input an initial converting list of the strings
+    Note, the converting list starts off with
+    ["internal boundary", "external boundary", "internal boundary"]
+    example input and output 
+    input ["a","b","a","c"],["c"]
+    output [[2, 1, 2, 0], ['c', 'b', 'a']]
+
+    the first element in the converting list will be
+    overwritten with "".
+    ?This will become the third item in the converting list?
+    
+    # note, order the initial converting list is important,
+     since the index = the int tag
+    """
+    nodups = unique(stringlist)
+    # note, order is important, the index = the int tag
+    #preset = ["internal boundary", "external boundary"]
+    #Remove the preset tags from the list with no duplicates
+    nodups = [x for x in nodups if x not in preset]
+
+    try:
+        nodups.remove("") # this has to go to zero
+    except ValueError:
+        pass
+    
+    # Add the preset list at the beginning of no duplicates
+    preset.reverse()
+    nodups.extend(preset)
+    nodups.reverse()
+
+        
+    convertstring2int = {}
+    convertint2string = []
+    index = 0
+    for x in nodups:
+        convertstring2int[x] = index
+        convertint2string.append(x)
+        index += 1
+    convertstring2int[""] = 0
+
+    intlist = []
+    for x in stringlist:
+        intlist.append(convertstring2int[x])
+    return [intlist, convertint2string]
+
+
+
+
+def unique(s):
+    """Return a list of the elements in s, but without duplicates.
+
+    For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
+    unique("abcabc") some permutation of ["a", "b", "c"], and
+    unique(([1, 2], [2, 3], [1, 2])) some permutation of
+    [[2, 3], [1, 2]].
+
+    For best speed, all sequence elements should be hashable.  Then
+    unique() will usually work in linear time.
+
+    If not possible, the sequence elements should enjoy a total
+    ordering, and if list(s).sort() doesn't raise TypeError it's
+    assumed that they do enjoy a total ordering.  Then unique() will
+    usually work in O(N*log2(N)) time.
+
+    If that's not possible either, the sequence elements must support
+    equality-testing.  Then unique() will usually work in quadratic
+    time.
+    """
+
+    n = len(s)
+    if n == 0:
+        return []
+
+    # Try using a dict first, as that's the fastest and will usually
+    # work.  If it doesn't work, it will usually fail quickly, so it
+    # usually doesn't cost much to *try* it.  It requires that all the
+    # sequence elements be hashable, and support equality comparison.
+    u = {}
+    try:
+        for x in s:
+            u[x] = 1
+    except TypeError:
+        del u  # move on to the next method
+    else:
+        return u.keys()
+
+    # We can't hash all the elements.  Second fastest is to sort,
+    # which brings the equal elements together; then duplicates are
+    # easy to weed out in a single pass.
+    # NOTE:  Python's list.sort() was designed to be efficient in the
+    # presence of many duplicate elements.  This isn't true of all
+    # sort functions in all languages or libraries, so this approach
+    # is more effective in Python than it may be elsewhere.
+    try:
+        t = list(s)
+        t.sort()
+    except TypeError:
+        del t  # move on to the next method
+    else:
+        assert n > 0
+        last = t[0]
+        lasti = i = 1
+        while i < n:
+            if t[i] != last:
+                t[lasti] = last = t[i]
+                lasti += 1
+            i += 1
+        return t[:lasti]
+
+    # Brute force is all that's left.
+    u = []
+    for x in s:
+        if x not in u:
+            u.append(x)
+    return u
+
+"""Refines triangles
+
+   Implements the #triangular bisection?# algorithm.
+ 
+   
+"""
+
+def Refine(mesh, triangles):
+    """
+    Given a general_mesh, and a triangle number, split
+    that triangle in the mesh in half. Then to prevent
+    vertices and edges from meeting, keep refining 
+    neighbouring triangles until the mesh is clean.
+    """
+    state = BisectionState(mesh)
+    for triangle in triangles:
+        if not state.refined_triangles.has_key(triangle):
+            triangle.rotate_longest_side()
+            state.start(triangle)
+            Refine_mesh(mesh, state)
+
+def Refine_mesh(mesh, state):
+    """
+    """
+    state.getState(mesh)
+    refine_triangle(mesh,state)
+    state.evolve()
+    if not state.end:
+        Refine_mesh(mesh,state)
+
+def refine_triangle(mesh,state):
+    split(mesh,state.current_triangle,state.new_point)
+    if state.case == 'one':
+        state.r[3]=state.current_triangle#triangle 2
+
+        new_triangle_id = len(mesh.meshTriangles)-1
+        new_triangle = mesh.meshTriangles[new_triangle_id]
+
+        split(mesh,new_triangle,state.old_point)
+        state.r[2]=new_triangle#triangle 1.2
+        state.r[4]=mesh.meshTriangles[len(mesh.meshTriangles)-1]#triangle 1.1
+        r = state.r
+        state.repairCaseOne()
+
+    if state.case == 'two':
+        state.r[2]=mesh.meshTriangles[len(mesh.meshTriangles)-1]#triangle 1
+
+        new_triangle = state.current_triangle
+
+        split(mesh,new_triangle,state.old_point)
+
+        state.r[3]=mesh.meshTriangles[len(mesh.meshTriangles)-1]#triangle 2.1
+        state.r[4]=new_triangle#triangle 2.2
+        r = state.r
+
+        state.repairCaseTwo()
+
+    if state.case == 'vertex':
+        state.r[2]=state.current_triangle#triangle 2
+        state.r[3]=mesh.meshTriangles[len(mesh.meshTriangles)-1]#triangle 1
+        r = state.r
+        state.repairCaseVertex()
+        
+    if state.case == 'start':
+        state.r[2]=mesh.meshTriangles[len(mesh.meshTriangles)-1]#triangle 1
+        state.r[3]=state.current_triangle#triangle 2
+
+    if state.next_case == 'boundary':
+        state.repairCaseBoundary()
+
+
+def split(mesh, triangle, new_point):
+	"""
+	Given a mesh, triangle_id and a new point,
+	split the corrosponding triangle into two
+	new triangles and update the mesh.
+	"""
+
+	new_triangle1 = Triangle(new_point,triangle.vertices[0],triangle.vertices[1],attribute = triangle.attribute, neighbors = None)
+	new_triangle2 = Triangle(new_point,triangle.vertices[2],triangle.vertices[0],attribute = triangle.attribute, neighbors = None)
+
+        new_triangle1.setNeighbors(triangle.neighbors[2],None,new_triangle2)
+        new_triangle2.setNeighbors(triangle.neighbors[1],new_triangle1,None)
+
+        mesh.meshTriangles.append(new_triangle1)
+
+        triangle.vertices = new_triangle2.vertices
+        triangle.neighbors = new_triangle2.neighbors
+
+
+class State:
+
+    def __init__(self):
+        pass
+
+class BisectionState(State):
+
+
+    def __init__(self,mesh):
+        self.len = len(mesh.meshTriangles)
+        self.refined_triangles = {}
+        self.mesh = mesh
+        self.current_triangle = None
+        self.case = 'start'
+        self.end = False
+        self.r = [None,None,None,None,None]
+
+    def start(self, triangle):
+        self.current_triangle = triangle
+        self.case = 'start'
+        self.end = False
+        self.r = [None,None,None,None,None]
+
+    def getState(self,mesh):
+        if not self.case == 'vertex':
+            self.new_point=self.getNewVertex(mesh, self.current_triangle)
+            #self.neighbour=self.getNeighbour(mesh, self.current_triangle)
+            self.neighbour = self.current_triangle.neighbors[0]
+            if not self.neighbour is None:
+                self.neighbour.rotate_longest_side()
+            self.next_case = self.get_next_case(mesh,self.neighbour,self.current_triangle)
+        if self.case == 'vertex':
+            self.new_point=self.old_point
+
+
+    def evolve(self):
+        if self.case == 'vertex':
+            self.end = True
+
+        self.last_case = self.case
+        self.case = self.next_case
+
+        self.old_point = self.new_point
+        self.current_triangle = self.neighbour
+
+        if self.case == 'boundary':
+            self.end = True
+        self.refined_triangles[self.r[2]]=1
+        self.refined_triangles[self.r[3]]=1
+        if not self.r[4] is None:
+            self.refined_triangles[self.r[4]]=1
+        self.r[0]=self.r[2]
+        self.r[1]=self.r[3]
+
+
+    def getNewVertex(self,mesh,triangle):
+	coordinate1 = triangle.vertices[1]
+	coordinate2 = triangle.vertices[2]
+	a = ([coordinate1.x*1.,coordinate1.y*1.])
+	b = ([coordinate2.x*1.,coordinate2.y*1.])
+        attributes = []
+        for i in range(len(coordinate1.attributes)):
+            att = (coordinate1.attributes[i]+coordinate2.attributes[i])/2
+            attributes.append(att)
+	new_coordinate = [((a[0]-b[0])/2+b[0]),((a[1]-b[1])/2+b[1])]
+	newVertex = Vertex(new_coordinate[0],new_coordinate[1], attributes = attributes)
+        mesh.maxVertexIndex+=1
+        newVertex.index = mesh.maxVertexIndex
+        mesh.meshVertices.append(newVertex)
+	return newVertex
+
+    def get_next_case(self, mesh,neighbour,triangle):
+	"""
+	Given the locations of two neighbouring triangles, 
+	examine the interior indices of their vertices (i.e. 
+	0,1 or 2) to determine what how the neighbour needs
+	to be refined.
+	"""
+	if (neighbour is None):
+		next_case = 'boundary'
+	else:
+		if triangle.vertices[1].x==neighbour.vertices[2].x:
+		    if triangle.vertices[1].y==neighbour.vertices[2].y:
+			next_case = 'vertex'
+                if triangle.vertices[1].x==neighbour.vertices[0].x:
+		    if triangle.vertices[1].y==neighbour.vertices[0].y:
+			next_case = 'two'
+		if triangle.vertices[1].x==neighbour.vertices[1].x:
+		    if triangle.vertices[1].y==neighbour.vertices[1].y:
+			next_case = 'one'
+	return next_case
+
+
+
+    def repairCaseVertex(self):
+
+        r = self.r
+
+
+        self.link(r[0],r[2])
+        self.repair(r[0])
+
+        self.link(r[1],r[3])
+        self.repair(r[1])
+
+        self.repair(r[2])
+
+        self.repair(r[3])
+
+
+    def repairCaseOne(self):
+        r = self.rkey
+
+
+        self.link(r[0],r[2])
+        self.repair(r[0])
+
+        self.link(r[1],r[4])
+        self.repair(r[1])
+
+        self.repair(r[4])
+
+    def repairCaseTwo(self):
+        r = self.r
+
+        self.link(r[0],r[4])
+        self.repair(r[0])
+
+        self.link(r[1],r[3])
+        self.repair(r[1])
+
+        self.repair(r[4])
+
+    def repairCaseBoundary(self):
+        r = self.r
+        self.repair(r[2])
+        self.repair(r[3])
+
+
+
+    def repair(self,triangle):
+        """
+        Given a triangle that knows its neighbours, this will
+        force the neighbours to comply.
+
+        However, it needs to compare the vertices of triangles
+        for this implementation 
+
+        But it doesn't work for invalid neighbour structures
+        """
+        n=triangle.neighbors
+        for i in (0,1,2):
+            if not n[i] is None:
+                for j in (0,1,2):#to find which side of the list is broken
+                    if not (n[i].vertices[j] in triangle.vertices):
+                    #ie if j is the side of n that needs fixing
+                        k = j
+                n[i].neighbors[k]=triangle
+
+
+
+    def link(self,triangle1,triangle2):
+        """
+        make triangle1 neighbors point to t
+                #count = 0riangle2
+        """
+        count = 0
+        for i in (0,1,2):#to find which side of the list is broken
+            if not (triangle1.vertices[i] in triangle2.vertices):
+                j = i
+                count+=1
+        assert count == 1
+        triangle1.neighbors[j]=triangle2
+
+class Discretised_Tuple_Set:
+    """
+    if a={(0.0):[(0.01),(0.02)],(0.2):[(0.17)]}
+    a[(0.01)]=a[(0.0)]=[(0.01),(0.02)]
+    a[(10000)]=[] #NOT KEYERROR
+
+    a.append[(0.01)]
+    => {0.0:[(0.01),(0.02),(0.01)],0.2:[(0.17)]}
+
+    #NOT IMPLIMENTED
+    a.remove[(0.01)]
+    => {(0.0):[(0.02),(0.01)],0.2:[(0.17)]}
+
+    a.remove[(0.17)]
+    => {(0.0):[(0.02),(0.01)],0.2:[]}
+    #NOT IMPLIMENTED
+    at a.precision = 2:
+        a.round_up_rel[0.0]=
+        a.round_flat[0.0]=
+        a.round_down_rel[0.0]=
+
+        a.up((0.1,2.04))=
+
+    If t_rel = 0, nothing gets rounded into
+    two bins. If t_rel = 0.5, everything does.
+
+    Ideally, precision can be set high, so that multiple
+    entries are rarely in the same bin. And t_rel should
+    be low (<0.1 for 1 dimension!,<(0.1/n) for small n!!)
+    so that it is rare to put items in mutiple bins.
+
+    Ex bins per entry = product(a,b,c...,n)
+    a = 1 or 2 s.t. Ex(a) = 1+2*t_rel
+    b = 1 or 2 ... 
+
+    BUT!!! to avoid missing the right bin:
+    (-10)**(precision+1)*t_rel must be greater than the 
+    greatest possible variation that an identical element
+    can display.
+
+
+    Note that if tol = 0.5 (the max allowed) 0.6 will round to .7 and .5
+    but not .6 - this looks wrong, but note that *everything* will round,
+    so .6 wont be missed as everything close to it will check in .7 and .5.
+    """
+    def __init__(self,p_rel = 6,t_rel = 0.01):
+        self.__p_rel__ = p_rel
+        self.__t_rel__ = t_rel
+
+        self.__p_abs__ = p_rel+1
+        self.__t_abs__ = t_rel
+
+        assert t_rel <= 0.5
+        self.__items__ = {}
+        from math import frexp
+        self.frexp = frexp
+        roundings = [self.round_up_rel,\
+        self.round_down_rel,self.round_flat_rel,\
+        self.round_down_abs,self.round_up_abs,\
+        self.round_flat_abs]#
+
+        self.roundings = roundings
+
+    def __repr__(self):
+        return '%s'%self.__items__
+
+    def rounded_keys(self,key):
+        key = tuple(key)
+        keys = [key]
+        keys = self.__rounded_keys__(key)
+        return (keys)
+
+    def __rounded_keys__(self,key):
+        keys = []
+        rounded_key=list(key)
+        rounded_values=list(key)
+
+        roundings = list(self.roundings)
+
+        #initialise rounded_values
+        round = roundings.pop(0)
+        for i in range(len(rounded_values)):
+            rounded_key[i]=round(key[i])
+            rounded_values[i]={}
+            rounded_values[i][rounded_key[i]]=None
+        keys.append(tuple(rounded_key))
+        
+        for round in roundings:
+            for i in range(len(rounded_key)):
+                rounded_value=round(key[i])
+                if not rounded_values[i].has_key(rounded_value):
+                    #ie unless round_up_rel = round_down_rel
+                    #so the keys stay unique
+                    for j in range(len(keys)):
+                        rounded_key = list(keys[j])
+                        rounded_key[i]=rounded_value
+                        keys.append(tuple(rounded_key))
+        return keys
+
+    def append(self,item):
+        keys = self.rounded_keys(item)
+        for key in keys:
+            if self.__items__.has_key(key):
+                self.__items__[key].append(item)
+            else:
+                self.__items__[key]=[item]
+
+    def __getitem__(self,key):
+        answer = []
+        keys = self.rounded_keys(key)
+        for key in keys:
+            if self.__items__.has_key(key):
+                answer.extend(self.__items__[key])
+        #if len(answer)==0:
+        #    raise KeyError#FIXME or return KeyError
+        #                  #FIXME or just return []?
+        else:
+            return answer #FIXME or unique(answer)?
+
+    def __delete__(self,item):
+        keys = self.rounded_keys(item)
+        answer = False
+        #if any of the possible keys contains
+        #a list, return true
+        for key in keys:        
+            if self.__items__.has_key(key):
+                if item in self.__items__[key]:
+                    self.__items__[key].remove(item)
+
+    def remove(self,item):
+        self.__delete__(item)
+
+    def __contains__(self,item):
+
+        keys = self.rounded_keys(item)
+        answer = False
+        #if any of the possible keys contains
+        #a list, return true
+        for key in keys:        
+            if self.__items__.has_key(key):
+                if item in self.__items__[key]:
+                    answer = True
+        return answer
+
+
+    def has_item(self,item):
+        return self.__contains__(item)
+
+    def round_up_rel2(self,value):
+         t_rel=self.__t_rel__
+         #Rounding up the value
+         m,e = self.frexp(value)
+         m = m/2
+         e = e + 1
+         #m is the mantissa, e the exponent
+         # 0.5 < |m| < 1.0
+         m = m+t_rel*(10**-(self.__p_rel__))
+         #bump m up
+         m = round(m,self.__p_rel__)
+         return m*(2.**e)
+
+    def round_down_rel2(self,value):
+         t_rel=self.__t_rel__
+         #Rounding down the value
+         m,e = self.frexp(value)
+         m = m/2
+         e = e + 1
+         #m is the mantissa, e the exponent
+         # 0.5 < m < 1.0
+         m = m-t_rel*(10**-(self.__p_rel__))
+         #bump the |m| down, by 5% or whatever
+         #self.p_rel dictates
+         m = round(m,self.__p_rel__)
+         return m*(2.**e)
+
+    def round_flat_rel2(self,value):
+    #redundant
+         m,e = self.frexp(value)
+         m = m/2
+         e = e + 1
+         m = round(m,self.__p_rel__)
+         return m*(2.**e)
+
+    def round_up_rel(self,value):
+         t_rel=self.__t_rel__
+         #Rounding up the value
+         m,e = self.frexp(value)
+         #m is the mantissa, e the exponent
+         # 0.5 < |m| < 1.0
+         m = m+t_rel*(10**-(self.__p_rel__))
+         #bump m up
+         m = round(m,self.__p_rel__)
+         return m*(2.**e)
+
+    def round_down_rel(self,value):
+         t_rel=self.__t_rel__
+         #Rounding down the value
+         m,e = self.frexp(value)
+         #m is the mantissa, e the exponent
+         # 0.5 < m < 1.0
+         m = m-t_rel*(10**-(self.__p_rel__))
+         #bump the |m| down, by 5% or whatever
+         #self.p_rel dictates
+         m = round(m,self.__p_rel__)
+         return m*(2.**e)
+
+    def round_flat_rel(self,value):
+    #redundant
+         m,e = self.frexp(value)
+         m = round(m,self.__p_rel__)
+         return m*(2.**e)
+
+    def round_up_abs(self,value):
+         t_abs=self.__t_abs__
+         #Rounding up the value
+         m = value+t_abs*(10**-(self.__p_abs__))
+         #bump m up
+         m = round(m,self.__p_abs__)
+         return m
+
+    def round_down_abs(self,value):
+         t_abs=self.__t_abs__
+         #Rounding down the value
+         m = value-t_abs*(10**-(self.__p_abs__))
+         #bump the |m| down, by 5% or whatever
+         #self.p_rel dictates
+         m = round(m,self.__p_abs__)
+         return m
+
+    def round_flat_abs(self,value):
+    #redundant?
+         m = round(value,self.__p_abs__)
+         return m
+
+    def keys(self):
+        return self.__items__.keys()
+
+
+class Mapped_Discretised_Tuple_Set(Discretised_Tuple_Set):
+    """
+    This is a discretised tuple set, but 
+    based on a mapping. The mapping MUST
+    return a sequence.
+
+    example: 
+    def weight(animal):
+        return [animal.weight]
+    
+    a = Mapped_Discretised_Tuple_Set(weight)
+    a.append[cow]
+    a.append[fox]
+    a.append[horse]
+
+    a[horse]    -> [cow,horse]
+    a[dog]      -> [fox]
+    a[elephant] -> []
+    """
+    def __init__(self,mapping,p_rel = 6, t_rel=0.01):
+        Discretised_Tuple_Set.__init__\
+         (self,p_rel,t_rel = t_rel)
+        self.mapping = mapping
+
+    def rounded_keys(self,key):
+        mapped_key = tuple(self.mapping(key))
+        keys = self.__rounded_keys__(mapped_key)
+        return keys
+
+class Affine_Linespace(Mapped_Discretised_Tuple_Set):
+    """
+    The affine linespace creates a way to record and compare lines.
+    Precision is a bit of a hack, but it creates a way to avoid 
+    misses caused by round offs (between two lines of different
+    lenghts, the short one gets rounded off more). 
+    I am starting to think that a quadratic search would be faster.
+    Nearly.
+    """
+    def __init__(self,p_rel=4,t_rel=0.2):
+        Mapped_Discretised_Tuple_Set.__init__\
+            (self,self.affine_line,\
+            p_rel=p_rel,t_rel=t_rel)
+
+        roundings = \
+        [self.round_down_rel,self.round_up_rel,self.round_flat_rel]
+        self.roundings = roundings
+        #roundings = \
+        #[self.round_down_abs,self.round_up_abs,self.round_flat_abs]
+        #self.roundings = roundings
+
+    def affine_line(self,line):
+        point_1 = line[0]
+        point_2 = line[1]
+        #returns the equation of a line
+        #between two points, in the from
+        #(a,b,-c), as in ax+by-c=0
+        #or line *dot* (x,y,1) = (0,0,0)
+
+        #Note that it normalises the line
+        #(a,b,-c) so Line*Line = 1.
+        #This does not change the mathematical
+        #properties, but it makes comparism
+        #easier.
+
+        #There are probably better algorithms.
+        x1 = point_1[0]
+        x2 = point_2[0]
+        y1 = point_1[1]
+        y2 = point_2[1]
+        dif_x = x1-x2
+        dif_y = y1-y2
+
+        if dif_x == dif_y == 0:
+            msg = 'points are the same'
+            raise msg
+        elif abs(dif_x)>=abs(dif_y):
+            alpha = (-dif_y)/dif_x
+            #a = alpha * b
+            b = -1.
+            c = (x1*alpha+x2*alpha+y1+y2)/2.
+            a = alpha*b
+        else:
+            beta = dif_x/(-dif_y)
+            #b = beta * a
+            a = 1.
+            c = (x1+x2+y1*beta+y2*beta)/2.
+            b = beta*a
+        mag = abs(a)+abs(b)
+        #This does not change the mathematical
+        #properties, but it makes comparism possible.
+
+        #note that the gradient is b/a, or (a/b)**-1.
+        #so 
+
+        #if a == 0:
+        #    sign_a = 1.
+        #else:
+        #    sign_a = a/((a**2)**0.5)
+        #if b == 0:
+        #    sign_b = 1.
+        #else:
+        #    sign_b = b/((b**2)**0.5)
+        #if c == 0:
+        #    sign_c = 1.
+        #else:
+        #    sign_c = c/((c**2)**0.5)
+        #a = a/mag*sign_a
+        #b = b/mag*sign_b
+        #c = c/mag*sign_c
+        a = a/mag
+        b = b/mag
+        c = c/mag
+        return a,b,c
+
+
+
+if __name__ == "__main__":
+    #from mesh import *
+    # THIS CAN BE DELETED
+    m = Mesh()
+    dict = importUngenerateFile("ungen_test.txt")
+    m.addVertsSegs(dict)
+    print m3
Index: /inundation/pmesh/pmesh.py
===================================================================
--- /inundation/pmesh/pmesh.py	(revision 1719)
+++ /inundation/pmesh/pmesh.py	(revision 1719)
@@ -0,0 +1,2022 @@
+
+import  Pmw, AppShell, math, time, string, marshal
+from toolbarbutton import ToolBarButton
+import tkFileDialog
+from   tkSimpleDialog import Dialog
+import mesh
+from Tkinter import  FALSE,TRUE, Frame,X, LEFT,YES,BOTH,ALL,Widget,CURRENT, Label,W, Entry, E, StringVar, END, Checkbutton, Radiobutton, IntVar, DISABLED, NORMAL
+#from cursornames import TLC,TRC, BLC, BRC, TS, RS, LS, BS
+from tkMessageBox import showerror, _show, QUESTION,YESNOCANCEL
+import types
+import visualmesh
+import os
+import profile
+import load_mesh.loadASCII
+from alpha_shape.alpha_shape import AlphaError
+
+# CONSTANTS 
+VERT_SELECT_ADDING_SEG_COLOR = 'orange'
+SELECT_COLOR = 'red'
+SEG_COLOUR = 'blue'
+TRIANGLE_COLOUR = 'green'
+APPLICATION_NAME = 'Pmesh'
+SET_COLOUR = 'red'
+DEFAULT_ATTRIBUTE = 'elevation'
+
+#for alpha shapes
+NO_SELECTION = 0
+AUTO = 1
+SET_ALPHA = 2
+
+
+class Draw(AppShell.AppShell):
+    usecommandarea = 1
+    appname        = APPLICATION_NAME
+    frameWidth     = 840
+    frameHeight    = 600
+    
+    
+    def createButtons(self):
+        """
+        Add buttons to the bottom of the GUI
+        """
+        self.buttonAdd('Postscript',
+              helpMessage='Save current drawing (as PostScript)',
+              statusMessage='',
+              command=self.ipostscript)
+        self.buttonAdd('Clear', helpMessage='Delete the mesh',
+              statusMessage='', command=self.clearMesh)
+        self.buttonAdd('Close', helpMessage='Close Screen',
+              statusMessage='', command=self.close)
+        
+    def createBase(self):
+        """
+        Create the GUI framework.  Set up the GUI
+        """
+        self.toolbar = self.createcomponent('toolbar', (), None,
+                  Frame, (self.interior(),), background="gray90")
+        self.toolbar.pack(fill=X)
+
+        self.scrolledcanvas =  self.createcomponent('ScrolledCanvas', (), None,
+                                       Pmw.ScrolledCanvas, (self.interior(),)
+                                       ,borderframe = 1
+                                       ,labelpos = 'n'
+                                       )
+        self.scrolledcanvas.configure(hscrollmode = 'dynamic')
+        self.scrolledcanvas.configure(vscrollmode = 'dynamic')
+        self.scrolledcanvas.pack(side=LEFT, expand=YES, fill=BOTH)
+        self.canvas = self.scrolledcanvas.component('canvas')
+        self.canvas.configure( background="white" )
+        
+        self.canvas.pack(side=LEFT, expand=YES, fill=BOTH)
+
+        Widget.bind(self.canvas, "<Button-1>", self.mouseDown)
+        Widget.bind(self.canvas, "<Button3-ButtonRelease>", self.rightMouseUp)
+        Widget.bind(self.canvas, "<Button2-ButtonRelease>",self.DeleteSelectedMeshObject)
+        # "<Delete>" didn't work..
+        #Widget.bind(self.canvas, "<Delete>", self.DeleteSelectedMeshObject)
+
+        #self.root.bind("<KeyPress>", self.setRegular)
+        #self.root.bind("<KeyRelease>", self.setRegular)
+        
+	self.scrolledcanvas.resizescrollregion()
+
+#     def setRegular(self, event):
+#         if event.type == '2' and event.keysym == 'Shift_L':
+#             self.regular = TRUE
+#         else:
+#             self.regular = FALSE
+
+    def createMenus(self):
+        """
+        Add menus to the top of the GUI
+        """
+        self.menuBar.deletemenuitems('File',0)
+        self.menuBar.addmenuitem('File', 'command', 'New mesh',
+                                 label='New', command=self.clearMesh)
+        self.menuBar.addmenuitem('File', 'command', 'Open mesh',
+                                 label='Open...', command=self.importFile)
+        self.menuBar.addmenuitem('File', 'command', 'Save mesh',
+                                 label='Save', command=self.saveDrawing)
+        self.menuBar.addmenuitem('File', 'command', 'Save mesh',
+                                 label='SaveAs...', command=self.saveAsDrawing)
+        
+        self.menuBar.addmenuitem('File', 'separator')
+        self.menuBar.addmenuitem('File', 'command',
+                                 'Add ungenerated file from arcGIS',
+                                 label='Add ungenerated file...',
+                                 command=self.ImportUngenerate)
+        
+        self.menuBar.addmenuitem('File', 'command',
+                                 'Export ASCII obj',
+                                 label='Export ASCII obj',
+                                 command=self.exportObj)
+        
+        self.menuBar.addmenuitem('File', 'command',
+                                 'Export ASCII segment outline',
+                                 label='Export ASCII segment outline...',
+                                 command=self.exportASCIIsegmentoutlinefile)
+        
+        self.menuBar.addmenuitem('File', 'command',
+                                 'Export ASCII xya file',
+                                 label='Export ASCII xya file...',
+                                 command=self.exportPointsFile)
+        
+        self.menuBar.addmenuitem('File', 'command',
+                                 'add Segments to connect all vertices'  ,
+                                 label='join vertices',
+                                 command=self.joinVertices)
+        self.menuBar.addmenuitem('File', 'command',
+                                 'add Segments to form alpha shape'  ,
+                                 label='Auto segment',
+                                 command=self.autoSegment)
+        self.menuBar.addmenuitem('File', 'command',
+                           'modify the alpha boundary by applying filters',
+                                 label='filter alpha boundary',
+                                 command=self.autoSegmentFilter)
+        self.menuBar.addmenuitem('File', 'command', 'Normalise mesh',
+                                 label='Normalise mesh', command=self.normaliseMesh)
+        self.menuBar.addmenuitem('File', 'command', 'Normalise mesh for glutobj',
+                                 label='Normalise mesh for glutobj', command=self.normalise4ObjMesh)
+        self.menuBar.addmenuitem('File', 'separator')
+        self.menuBar.addmenuitem('File', 'command', '',
+                                 label='Print geo reference', command=self.printGeoReference)
+        self.menuBar.addmenuitem('File', 'separator')
+        self.menuBar.addmenuitem('File', 'command', 'Exit program',
+                                 label='Exit', command=self.quit)
+
+    def createTools(self):
+        """
+        Add buttons to the top of the GUI
+        """
+        self.mouseDownFunc = {}
+        self.modeClass = {}
+        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif',
+                      width=10, state='disabled')
+        for key, balloon, mouseDownFunc, Mode in [
+            ('pointer','Edit drawing eventually.  Right now this does nothing', self.drag, None)
+            ,('vertex',    'Vertex mode', self.drawVertex, mesh.Vertex)
+            ,('segment', 'Segment mode',self.selectSegmentPoint, mesh.Segment)
+            ,('hole', 'hole mode',self.drawHole, mesh.Hole)
+            ,('region', 'region mode',self.drawRegion, mesh.Region)
+            ]:
+            t = ToolBarButton(self, self.toolbar, key, '%s.gif' % key,
+                          command=self.selectFunc, balloonhelp=balloon,
+                               statushelp='')
+            t.cycle("DrawMode")
+            if key == 'pointer': #FIXME- this is specified in line 1062 as well
+                                 # self.selectFunc('pointer')
+                self.curFunc  = self.drawVertex
+                t.setInitialSunkenButton("DrawMode")
+            self.modeClass[key] = Mode
+            # for actions that occur when the mouse goes down
+            self.mouseDownFunc[key] = mouseDownFunc
+         
+    def createZooms(self):
+        """
+        Add zoom buttons to the top of the GUI
+        """
+        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
+                      state='disabled')
+        zoom = '0.5'
+        ToolBarButton(self, self.toolbar, zoom, 'zoom%s.gif' %
+                      zoom, command=self.selectZoom,
+                      balloonhelp='*%s zoom' % zoom,
+                      statushelp='')
+            
+        ToolBarButton(self, self.toolbar,'1.0', 'zoomToMesh.gif',
+                      command=self.ResizeToFitWrapper,
+                      balloonhelp='Zooms to mesh size',
+                      statushelp='')
+        zoom = '2'
+        ToolBarButton(self, self.toolbar, zoom, 'zoom%s.gif' %
+                      zoom, command=self.selectZoom,
+                      balloonhelp='*%s zoom' % zoom,
+                      statushelp='')
+
+    def createEdits(self):
+        """
+        Add Edit buttons to the top of the GUI
+        """
+        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
+                      state='disabled')
+        for key, func, balloon in [
+                ('addVertex', self.windowAddVertex, 'add Vertex'),
+                ('edit', self.windowEdit, 'edit selected object'),
+                ('default', self.windowDefault, 'set default value for selected mode'),
+                ('joinVer', self.joinVerticesButton, 'add Segments to connect all vertices'),      
+             #   ('autoSegHull', self.autoSegmentHullButton, 'add Segments to form alpha shape, using Hull'),  
+             #   ('autoSeg', self.autoSegmentButton, 'add Segments to form alpha shape'), 
+                ('autoSegGiveAlpha', self.autoSegmentGiveAlphaButton, 'add Segments to form alpha shape, specify alpha'),
+                ('meshGen', self.windowMeshGen, 'Generate Mesh')]:
+            ToolBarButton(self, self.toolbar, key, '%s.gif' % key,
+                          command=func, balloonhelp=balloon,
+                               statushelp='' )
+
+
+
+    def createSetTools(self):
+        """
+        Add set tool buttons to the top of the GUI
+        """
+        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
+                      state='disabled')
+        for key, func, balloon in [
+                ('threshold', self.threshold, 'threshold the set'),
+                ('Courant_threshold', self.Courant_threshold, 'Courant_threshold the set'),
+                ('gradient_threshold', self.gradient_threshold, 'gradient_threshold the set'),
+                ('smooth', self.smooth_polySet, 'smooth the polygons'),
+                ('polyset', self.triangles_to_polySet, 'make a poly set out of selected triangles')]:      
+                #('refineSet', self.refineSet, 'Refine the set')]:
+            ToolBarButton(self, self.toolbar, key, '%s.gif' %key,
+                          command=func, balloonhelp=balloon,
+                               statushelp='' )
+
+    def createSetIcons(self):
+        """
+        Add Edit buttons to the top of the GUI
+        """
+        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
+                      state='disabled')
+        for key, func, balloon in [
+                ('selectAllTriangles', self.selectAllTriangles, 'select all'),
+                ('none', self.clearSelection, 'clear selection')]:
+            ToolBarButton(self, self.toolbar, key, '%s.gif' %key,
+                          command=func, balloonhelp=balloon,
+                               statushelp='' )
+
+    def createVisualiseIcons(self):
+        """
+        Add Edit buttons to the top of the GUI
+        """
+        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
+                      state='disabled')
+        for key, func, balloon in [
+                ('visualise', self.visualise, 'Visualise mesh triangles'),
+                ('unvisualise', self.unvisualise, 'Do not visualise mesh triangles (for large meshes)')]:
+            ToolBarButton(self, self.toolbar, key, '%s.gif' %key,
+                          command=func, balloonhelp=balloon,
+                               statushelp='' )
+
+
+    def refineSet(self,parent):
+        self.mesh.refineSet(self.selSet)
+        self.visualiseMesh(self.mesh)
+
+
+    def setStructureNumber(self,parent):
+        dialog =  setStructureNumberDialog(self.canvas)
+        if dialog.numberOK:
+            self.structureSize = dialog.number
+
+    def erode(self, parent):
+#Not implimented
+        self.canvas.delete(ALL)
+        self.mesh.erode(self.selSet,structureSize=self.structureSize)
+        self.visualiseMesh(self.mesh)
+
+
+    def dilate(self, parent):
+#Not implimented
+        self.canvas.delete(ALL)
+        self.mesh.dilate(self.selSet,structureSize=self.structureSize)
+        self.visualiseMesh(self.mesh)
+
+    def general_threshold(self,parent,function,function_description):
+        """
+        add a vertex using a window and entering x y values.
+
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        if self.selSet == 'None':
+            self.selectAllTriangles(parent)
+
+        dialog = GeneralThresholdDialog(self.canvas,self.mesh.attributeTitles,function_description)
+        if dialog.minmaxValuesOk:
+            self.canvas.delete(ALL)
+            min = dialog.min
+            max = dialog.max
+            attribute_name = dialog.attribute_name
+            self.mesh.general_threshold(self.selSet,min=min,max=max,attribute_name = attribute_name,function=function)
+            self.visualiseMesh(self.mesh)
+
+    def threshold(self, parent):
+        """
+        add a vertex using a window and entering x y values.
+
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        function = self.mesh.av_att
+        function_description = 'average attribute of triangle'
+        self.general_threshold(parent,function,function_description)
+
+
+    def Courant_threshold(self, parent):
+        """
+        add a vertex using a window and entering x y values.
+
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        function = self.mesh.Courant_ratio
+        function_description = 'average attribute/area of triangle'
+        self.general_threshold(parent,function,function_description)
+
+    def gradient_threshold(self, parent):
+        """
+        add a vertex using a window and entering x y values.
+
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        function = self.mesh.Gradient
+        function_description = 'average gradient of triangle'
+        self.general_threshold(parent,function,function_description)
+
+    def smooth_polySet(self,parent):
+        dialog = SmoothDialog(self.canvas)
+        if dialog.valueOK:
+            min_radius = dialog.min_radius
+            self._smooth_polySet(min_radius)
+
+    def _smooth_polySet(self,min_radius):
+        userVertices,userSegments,alphaSegments = \
+            self.mesh.smooth_polySet(min_radius=min_radius)
+
+        self.mesh.userVertices=[]
+        self.mesh.userSegments=[]
+        self.mesh.alphaSegments=[]
+        self.canvas.delete(ALL)
+        event = None
+        print 'len(userVertices.keys())'
+        print len(userVertices.keys())
+        print 'len(userSegments.keys())'
+        print len(userSegments.keys())
+        print 'len(alphaSegments.keys())'
+        print len(alphaSegments.keys())
+
+        #######
+        point_keys = {}
+        for vert in userVertices.keys():
+            assert not point_keys.has_key((vert.x,vert.y))
+            point_keys[(vert.x,vert.y)]=vert
+        assert len(point_keys.keys())==len(userVertices.keys())
+        #######
+
+        for v in userVertices.keys():
+            x = v.x*self.SCALE
+            y = v.y*self.SCALE
+            userVertices[(v.x,v.y)]=self.drawVertex(x,y,event)
+
+        for line in userSegments.keys():
+            v0 = userVertices[line[0]]
+            v1 = userVertices[line[1]]
+            segment = self.drawSegment(v0,v1)
+            segment.set_tag(userSegments[line].tag)
+
+        for line in alphaSegments.keys():
+            v0 = userVertices[line[0]]
+            v1 = userVertices[line[1]]
+            segment = self.drawSegment(v0,v1)
+            segment.set_tag(alphaSegments[line].tag)
+        self.visualiseMesh(self.mesh)
+
+
+    def triangles_to_polySet(self,parent):
+        userVertices,userSegments,alphaSegments = \
+            self.mesh.triangles_to_polySet(self.selSet)
+
+        self.mesh.userVertices=[]
+        self.canvas.delete(ALL)
+
+        event = None
+        print 'len(userVertices.keys())'
+        print len(userVertices.keys())
+        print 'len(userSegments.keys())'
+        print len(userSegments.keys())
+        print 'len(alphaSegments.keys())'
+        print len(alphaSegments.keys())
+
+
+        #######
+        point_keys = {}
+        for vert in userVertices.keys():
+            assert not point_keys.has_key((vert.x,vert.y))
+            point_keys[(vert.x,vert.y)]=vert
+        assert len(point_keys.keys())==len(userVertices.keys())
+        #######
+
+        for v in userVertices.keys():
+            if userVertices[v] is True:
+                x = v.x*self.SCALE
+                y = v.y*self.SCALE
+                userVertices[(v.x,v.y)]=self.drawVertex(x,y,event)
+
+        for line in userSegments.keys():
+            v0 = userVertices[line[0]]
+            v1 = userVertices[line[1]]
+            segment = self.drawSegment(v0,v1)
+            segment.set_tag(userSegments[line].tag)
+
+        for line in alphaSegments.keys():
+            v0 = userVertices[line[0]]
+            v1 = userVertices[line[1]]
+            segment = self.drawSegment(v0,v1)
+            segment.set_tag(alphaSegments[line].tag)
+        self.visualiseMesh(self.mesh)
+        #self.smooth_polySet(parent)
+
+    def selectTriangles(self,setName):
+        """
+        """
+        self.canvas.delete(ALL)
+        self.selSet = setName
+        self.visualiseMesh(self.mesh)
+
+    def selectAllTriangles(self,parent):
+        """
+        selected all triangles in the mesh
+        """
+        self.canvas.delete(ALL)
+        self.selSet = self.mesh.selectAllTriangles()
+        self.visualiseMesh(self.mesh)
+
+    def clearSelection(self,parent):
+    #FIXME looks like self.clearSelections - change name (Peter)
+        """
+        """
+        self.canvas.delete(ALL)
+        self.selSet = self.mesh.clearSelection()
+        self.visualiseMesh(self.mesh)
+
+    def visualise(self,parent):
+        self.canvas.delete(ALL)
+        self.Visualise = True
+        self.visualiseMesh(self.mesh)
+
+    def unvisualise(self,parent):
+        self.canvas.delete(ALL)
+        self.Visualise = False
+        self.visualiseMesh(self.mesh)
+
+    def createMesh(self):
+        """
+        Build the data structures for storing the mesh objects
+        """
+        self.mesh = mesh.Mesh()
+        
+        self.Vertices = visualmesh.vPoints(mesh.Vertex)
+        self.Segments = visualmesh.vSegments(mesh.Segment)
+        self.Holes = visualmesh.vPoints(mesh.Hole)
+        self.Regions = visualmesh.vRegions(mesh.Region)
+        self.UserMesh = visualmesh.vMesh([self.Vertices,self.Segments,self.Holes,self.Regions])
+        
+        self.Triangles = visualmesh.vTriangles(mesh.Triangle)
+        self.selSet='None'
+
+
+    def deleteMesh(self):
+        """
+        Delete the data structures for storing the mesh objects
+        """
+        self.mesh = None
+        self.Vertices = None
+        self.Segments = None
+        self.Triangles = None
+        
+    def addCylinders(self):
+        """
+        Automatically add some verts and segs to the mesh.Used in Debugging
+        """
+        from coordinate_transforms.geo_reference import Geo_reference,DEFAULT_ZONE
+        offset_x = 30
+        offset_y = 40
+        v1 = self.drawVertex(0-offset_x,0-offset_y,None)
+        v2 = self.drawVertex(50-offset_x,2-offset_y,None)
+        v3 = self.drawVertex(70-offset_x,30-offset_y,None)
+        v4 = self.drawVertex(25-offset_x,50-offset_y,None)
+        v5 = self.drawVertex(-10-offset_x,20-offset_y,None)
+        #v5 = self.drawVertex(25,50,None)
+        s1 = self.drawSegment(v1,v2)
+        s1.set_tag("s1")
+        s2 = self.drawSegment(v2,v3)
+        s2.set_tag("s2")
+        s3 = self.drawSegment(v3,v4)
+        s3.set_tag("s3")
+        s4 = self.drawSegment(v4,v5)
+        s4.set_tag("s4")
+        s5 = self.drawSegment(v1,v5)
+        s5.set_tag("s5")
+
+        x_origin = 10-offset_x
+        y_origin = 20-offset_y
+        r = 10
+        pi = math.pi
+        num_of_cuts = 100
+        cuts = []
+        factor = 2* math.pi/num_of_cuts
+        for cut in range(num_of_cuts):
+             cuts.append(cut*factor)
+        
+        for radius in cuts:
+            x = x_origin + r * math.cos(radius)
+            y = y_origin + r * math.sin(radius)
+            v = self.drawVertex(x,y,None)
+            if not radius == 0.0:   # FIXME
+                self.drawSegment(v,v_old)
+            else:
+                v_first = v
+            v_old = v
+        self.drawSegment(v,v_first)
+        region = self.drawRegion(x_origin, y_origin, 0)
+        region.setTag("setheight5")
+        
+        
+        x_origin = 30-offset_x
+        y_origin = 30-offset_y
+        r = 5
+        pi = math.pi
+        num_of_cuts = 100
+        cuts = []
+        factor = 2* math.pi/num_of_cuts
+        for cut in range(num_of_cuts):
+             cuts.append(cut*factor)
+        
+        for radius in cuts:
+            x = x_origin + r * math.cos(radius)
+            y = y_origin + r * math.sin(radius)
+            v = self.drawVertex(x,y,None)
+            if not radius == 0.0:   # FIXME
+                self.drawSegment(v,v_old)
+            else:
+                v_first = v
+            v_old = v
+        self.drawSegment(v,v_first)
+        region = self.drawRegion(x_origin, y_origin, 0)
+        region.setTag("setheight10")
+        self.mesh.geo_reference = Geo_reference(zone=DEFAULT_ZONE,
+                                                xllcorner=offset_x,
+                                                yllcorner=offset_y)
+            
+        #Since the new vertex may be off screen
+        self.scrolledcanvas.resizescrollregion()
+
+        # generate the mesh
+        minAngle = 30.0
+        numTriangles = 20000
+        self.clearSelections()
+        self.canvas.delete(ALL)
+        self.mesh = self.MeshGenAreaNumTriangles (minAngle,
+                                          numTriangles,
+                                          self.mesh)
+        self.UserMeshChanged = False
+        self.visualiseMesh(self.mesh)
+        print "Mesh Generation finished"
+            
+    def selectFunc(self, tag):
+        """
+        Change the current mode class
+        When changing from one mode to another
+        """
+        self.mouseDownCurFunc = self.mouseDownFunc[tag]
+        self.curModeClass = self.modeClass[tag]
+        self.clearSelections()
+        self.canvas.config(cursor='arrow')
+#        I can make it arrow, but it will change back to pointer, after
+#        adding an object
+#        if self.curFunc == self.func['pointer']:
+#            self.canvas.config(cursor='arrow')
+#         else:
+#             self.canvas.config(cursor='crosshair')
+
+    def clearSelections(self):
+        """
+        deselect objects that have been selected 
+        """
+        if self.selMeshObject:
+            self.deselectMeshObject(self.selMeshObject, self.selMeshTag)       
+        if self.selVertex:
+            self.deselectVertex(self.selVertex, self.selVertexTag)
+          
+        
+    def selectZoom(self, tag):
+        """
+        Zoom in or out of the current mesh view
+        """
+        fraction = string.atof(tag)
+        self.SCALE *= fraction
+        self.scrolledcanvas.scale(ALL, 0, 0, fraction, fraction)
+
+        # Redraw all of the vertices, holes and regions,
+        #so the squares representing vertices
+        # don't get bigger
+        vertices = self.mesh.getUserVertices()
+        holes = self.mesh.getHoles()
+        regions = self.mesh.getRegions()
+        MeshObjects  = vertices + holes + regions
+
+        # make a list of tags to delete
+        guiIDs = [getattr(MeshObjects[i],'guiID') for i in xrange(len(MeshObjects))]
+        apply(self.canvas.delete, guiIDs)
+        for obj in MeshObjects:
+            if self.selVertex == obj:
+                obj.draw(self.canvas,obj.guiID,  scale =self.SCALE ,colour= VERT_SELECT_ADDING_SEG_COLOR)
+            elif self.selMeshObject == obj:
+                obj.draw(self.canvas,obj.guiID,  scale =self.SCALE ,colour= SELECT_COLOR)
+            else:
+                obj.draw(self.canvas,obj.guiID,  scale =self.SCALE ) 
+        top, bottom = self.scrolledcanvas.xview()
+        xcenter  = (top + bottom)/2
+        xdiff =  xcenter - top  
+        xcnew = xcenter - xdiff/fraction
+        
+        top, bottom = self.scrolledcanvas.yview()
+        ycenter = (top + bottom)/2
+        ydiff = ycenter - top
+        ycnew = ycenter - ydiff/fraction
+        
+        self.scrolledcanvas.resizescrollregion()
+        # update so the moveto calls will work...
+        self.scrolledcanvas.update()
+        # but calling update now does make things jerky
+        self.canvas.xview_moveto(xcnew)
+        self.canvas.yview_moveto(ycnew)
+        
+    
+    def windowAddVertex (self, parent):
+        """
+        add a vertex using a window and entering x y values.
+
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        
+        dialog = AddVertexDialog(self.canvas)
+        if dialog.xyValuesOk:
+            print dialog.x
+            print dialog.y
+            self.drawVertex(dialog.x*self.SCALE,dialog.y*self.SCALE,None)
+            #Since the new vertex may be off screen
+            self.ResizeToFit()
+        else:
+            print "bad values"
+    
+    def windowDefault (self, parent):
+        """
+        
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        # self.UserMesh is a vMesh instance
+        self.UserMesh.defaultWindow(self.canvas, self.curModeClass)
+    
+    def windowEdit (self, parent):
+        """
+
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        if self.selMeshObject:   
+            self.UserMeshChanged = self.UserMesh.editWindow(self.canvas,
+                                     self.selMeshObject,
+                                     self.UserMeshChanged)
+    
+    def autoSegmentButton (self, parent):
+        self.autoSegment()
+
+        
+    def autoSegmentGiveAlphaButton (self, parent):
+        dialog = AutoSegmentDialog(self.canvas, self.meshLastAlpha)
+        if dialog.use_optimum.get() == SET_ALPHA:
+            if dialog.alphaValueOk:
+                self.autoSegment(alpha = dialog.alpha,
+                                 raw_boundary=dialog.raw_boundary.get(),
+                                 remove_holes=dialog.remove_holes.get(),
+                                 smooth_indents=dialog.smooth_indents.get(),
+                                 expand_pinch=dialog.expand_pinch.get())
+            else:
+                 showerror('pMesh',
+                      'Bad alpha value.')
+        else:
+            self.autoSegment(raw_boundary=dialog.raw_boundary.get(),
+                             remove_holes=dialog.remove_holes.get(),
+                             smooth_indents=dialog.smooth_indents.get(),
+                             expand_pinch=dialog.expand_pinch.get())
+           
+        
+    def autoSegment (self, alpha = None,
+                                 raw_boundary=True,
+                                 remove_holes=False,
+                                 smooth_indents=False,
+                                 expand_pinch=False ):
+        """
+        add Segments to bound all vertices
+        
+        """
+        if len(self.mesh.getUserVertices()) >= 3:
+            try:
+                newsegs, ObjectsToVisuallyDelete, self.meshLastAlpha = \
+                     self.mesh.autoSegment(alpha=alpha,
+                                           remove_holes=remove_holes,
+                                           smooth_indents=smooth_indents,
+                                           expand_pinch=expand_pinch)
+            except AlphaError:
+                showerror('pMesh',
+                          'Unable to autosegment.')
+            else:
+
+                for drawOb in ObjectsToVisuallyDelete:
+                    self.UserMesh.unvisualise(drawOb, self.canvas)
+                
+                for segment in newsegs:
+                    self.serial +=1
+                    self.uniqueID = 'M*%d' % self.serial
+                    self.Segments.visualise(segment,
+                                            self.uniqueID,
+                                            self.canvas,
+                                            self.SCALE)
+            
+        else:
+            showerror('pMesh',
+                      'Three or more vetices are needed to be able to autosegment.')
+
+
+    def autoSegmentFilter (self):
+        dialog = AutoSegmentFilterDialog(self.canvas)
+        newsegs, ObjectsToVisuallyDelete, self.meshLastAlpha = \
+                 self.mesh.autoSegmentFilter(raw_boundary=dialog.raw_boundary.get(),
+                             remove_holes=dialog.remove_holes.get(),
+                             smooth_indents=dialog.smooth_indents.get(),
+                             expand_pinch=dialog.expand_pinch.get())
+        #print "newsegs",newsegs
+        #print "ObjectsToVisuallyDelete",ObjectsToVisuallyDelete
+            
+        for drawOb in ObjectsToVisuallyDelete:
+            self.UserMesh.unvisualise(drawOb, self.canvas)
+                
+        for segment in newsegs:
+            self.serial +=1
+            self.uniqueID = 'M*%d' % self.serial
+            self.Segments.visualise(segment,
+                                    self.uniqueID,
+                                    self.canvas,
+                                    self.SCALE)
+        
+    def joinVerticesButton (self, parent):
+        self.joinVertices()
+        
+    def joinVertices (self):
+        """
+        add Segments to connect all vertices
+        
+        the parent attribute isn't used by this function.
+        need to userstand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        if len(self.mesh.getUserVertices()) >= 3:
+            newsegs = self.mesh.joinVertices()
+            for segment in newsegs:
+                self.serial +=1
+                self.uniqueID = 'M*%d' % self.serial
+                self.Segments.visualise(segment,
+                                        self.uniqueID,
+                                        self.canvas,
+                                        self.SCALE)
+        else:
+            showerror('pMesh',
+                      'Three or more vetices are needed to be able to join vertices.')
+        
+    def windowMeshGen (self, parent):
+        """
+        The parent attribute isn't used by this function.
+        need to understand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        # Put exceptions round things.
+        #catch failure in self.mesh.generateMesh
+        dialog = MeshGenDialog(self.canvas,
+                               self.MeshMinAngle,
+                               self.MeshMaxArea,
+                               self.MeshnumTriangles,
+                               self.MeshMaxAreaLast)
+        
+        if dialog.ValuesOk:
+            print dialog.minAngle
+            print dialog.maxArea
+            
+            self.clearSelections()
+            self.canvas.delete(ALL)
+            if dialog.goodMaxArea == True:
+                self.MeshMinAngle = dialog.minAngle
+                self.MeshMaxArea = dialog.maxArea
+                self.MeshMaxAreaLast = True
+                
+                self.mesh = self.MeshGenAreaAngle (dialog.minAngle,
+                                          dialog.maxArea,
+                                          self.mesh)
+            elif dialog.goodNumTriangles == True:
+                self.MeshMinAngle = dialog.minAngle
+                self.MeshnumTriangles  = dialog.numTriangles
+                self.MeshMaxAreaLast = False
+                
+                self.mesh = self.MeshGenAreaNumTriangles (dialog.minAngle,
+                                          dialog.numTriangles,
+                                          self.mesh)
+            else:
+                pass
+            print "userMeshChanged = False"
+            self.UserMeshChanged = False
+            self.visualiseMesh(self.mesh)
+            print "Mesh Generation finished"
+            
+    def MeshGenAreaAngle (self, minAngle, maxArea, mesh):
+        """
+        Generate a mesh, given a minAngle and max area
+        """
+        tempMesh = mesh
+        try:
+            tempMesh.generateMesh(mode = "pzq"+str(minAngle)
+                                  +"a"+str(maxArea)
+                                  +"a") #So areas for regions will be used
+        except AttributeError : # can't catch PyEval_RestoreThread
+            # This doesn't catch tempMesh.generateMesh failing
+            tempMesh = mesh
+        return tempMesh
+        
+
+    def MeshGenAreaNumTriangles (self, minAngle, numTriangles, mesh):
+        """
+        Generate a mesh, given a minAngle and rough # of triangles
+        """
+        #get big triangles
+        #calc area
+        #calc max triangle area 
+        #
+        tempMesh = mesh
+        try:
+            tempMesh.generateMesh("pzq1")
+        except AttributeError : # can't catch PyEval_RestoreThread
+            # This doesn't catch tempMesh.generateMesh failing
+            pass
+        meshArea = 0
+        for triangle in tempMesh.getTriangulation():
+             meshArea += triangle.calcArea()
+        print "meshArea: ", meshArea
+
+        maxArea = meshArea/numTriangles
+
+        
+        return self.MeshGenAreaAngle (minAngle,
+                                      maxArea,
+                                      self.mesh)
+        
+    def mouseDown(self, event):
+        """
+        On a mouse down event, depending on the current state,
+        either add a vertex or a seg etc
+        """
+        self.curObject = None
+        self.lastx = self.startx = self.canvas.canvasx(event.x)
+        #The screen canvas has y 'flipped'.  -1* unflips it
+        self.lasty = self.starty = -1*self.canvas.canvasy(event.y)
+        print "----------------------"
+        self.mouseDownCurFunc( self.lastx,
+                               self.lasty,event) #!!! remove the event?
+                                                 # do last
+   
+    def rightMouseUp(self, event):
+        """
+        On a right mouse button up event select the nearest object.
+        """
+        found=False
+        if event.widget.find_withtag(CURRENT): # if there's a widget with a tag
+            [tag,string] = self.canvas.gettags(CURRENT) # get a list of them
+            print "tag",tag  #tags ('M*1008', 'current')
+            if tag[:2] == 'M*':   #!!! this can be removed when there are
+                #    only mesh objects on screen
+                #key, value = string.split(tag, '*')
+                objectID = tag
+                print "Found!! objectID:", objectID
+                
+                meshObjects = self.getAllUserMeshObjects()
+                # It may be a triangle, which is ignored
+                if meshObjects.hasKey(objectID):
+                    selMeshObject = meshObjects.getMeshObject(objectID)
+                    found = True
+                    print "Found! selMeshObject", selMeshObject
+                    #Only select one object at a time
+                    if self.selMeshObject:
+                        self.deselectMeshObject(self.selMeshObject, self.selMeshTag)
+                    self.selectMeshObject(selMeshObject,objectID)
+
+    def getAllUserMeshObjects(self):
+        return self.UserMesh
+        
+    def DeleteSelectedMeshObject(self, event):
+        """
+        if an object is selected, delete it.
+        """
+        if self.selMeshObject:
+            #an object is selected
+            #first deselect the vertex, for selecting a segment
+            if self.selVertex:
+                self.deselectVertex(self.selVertex, self.selVertexTag)
+            ObjectsToVisuallyDelete = self.mesh.deleteMeshObject (self.selMeshObject)
+            for drawOb in ObjectsToVisuallyDelete:
+                self.UserMesh.unvisualise(drawOb, self.canvas)
+                
+            self.selMeshObject = None
+            self.selMeshTag = None
+            
+    def selectMeshObject(self, meshObject, objectID):
+        """
+        selected a mesh object.
+        """
+        self.canvas.delete(objectID)
+        self.selMeshObject = meshObject
+        self.selMeshTag = objectID
+        meshObject.draw(self.canvas,objectID, scale =self.SCALE ,colour = SELECT_COLOR)
+    
+    def deselectMeshObject(self, meshObject, objectID):
+        """
+        deselected a mesh object.
+        """
+        self.canvas.delete(objectID)
+        self.selMeshObject = None
+        self.selMeshTag = None
+        if isinstance(meshObject, mesh.Segment):
+            meshObject.draw(self.canvas,objectID,
+                        scale =self.SCALE ,colour = SEG_COLOUR)
+        else:
+            meshObject.draw(self.canvas,objectID,
+                        scale =self.SCALE )
+            
+    def drag(self,x,y,event):
+        """
+        Hack function.  called when in select and left mouse goes down
+        """
+        pass
+    
+    
+    def drawEastingNorthingVertex(self,x,y,event):
+        """
+        draw a vertex object, plus add it to the mesh data structure
+
+        event isn't used
+        """
+        self.serial +=1
+        self.uniqueID = 'M*%d' % self.serial
+        #x_scaled =  self.SCALE*x
+        #y_scaled = -1*self.SCALE*y
+        #print "x y:", x,y
+        vert = self.Vertices.draw(x-self.mesh.geo_reference.get_xllcorner,
+                                  y-self.mesh.geo_reference.get_yllcorner,
+                                  self.mesh,
+                                  self.uniqueID,
+                                  self.SCALE,
+                                  self.canvas,
+                                  event) #FIXME why is event passed on.
+        self.UserMeshChanged = True
+        return vert
+    
+    def drawVertex(self,x,y,event):
+        """
+        draw a vertex object, plus add it to the mesh data structure
+
+        event isn't used
+        """
+        self.serial +=1
+        self.uniqueID = 'M*%d' % self.serial
+        #x_scaled =  self.SCALE*x
+        #y_scaled = -1*self.SCALE*y
+        #print "x y:", x,y
+        vert = self.Vertices.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event)
+        self.UserMeshChanged = True
+        return vert
+     
+    def drawHole(self,x,y,event):
+        """
+        draw a hole object, plus add it to the mesh data structure
+
+        event isn't used
+        """
+        self.serial +=1
+        self.uniqueID = 'M*%d' % self.serial
+        self.userMeshChanged = True
+        hole = self.Holes.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event)
+        return hole    
+    
+    def drawRegion(self,x,y,event):
+        """
+        draw a region object, plus add it to the mesh data structure
+
+        event isn't used
+        """
+        self.serial +=1
+        self.uniqueID = 'M*%d' % self.serial
+        region = self.Regions.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event)
+        return region
+    
+    def selectSegmentPoint(self,x,y, event):
+        """
+        logic when selecting a vertex object to add a segment
+        """
+        found=False
+        if event.widget.find_withtag(CURRENT): # if there's a widget with a tag
+            [tag,string] = self.canvas.gettags(CURRENT) # get a list of them
+            print "tag",tag  #tags ('M*1008', 'current')
+            objectID = tag
+            #print "Found!! objectID:", objectID
+            if self.Vertices.hasKey(objectID): #isinstance(self.meshObjects[objectID],mesh.Vertex):
+                vertex = self.Vertices.getMeshObject(objectID)
+                found = True
+                print "Found! vertex", vertex
+            
+        if found and self.selVertex == vertex:
+            print "The selected vertex has already been selected"
+            #The selected vertex has already been selected
+            # therefore deselect it
+            self.deselectVertex(self.selVertex, self.selVertexTag)
+            found = False
+                 
+        if found: 
+            #A vertex has been selected!
+            if self.selVertex:
+                if self.mesh.isUserSegmentNew(self.selVertex,vertex):
+                    #vertex is the 2nd vertex
+                    self.drawSegment(vertex,self.selVertex)
+                    self.deselectVertex(self.selVertex, self.selVertexTag)
+                    self.selectVertex(vertex,objectID)
+            else:
+                print "vertex is the 1st vertex" 
+                #vertex is the 1st vertex
+                self.selectVertex(vertex,objectID)
+        else:
+            print " There are no widgets.  This happen's too much"
+                    
+
+    def selectVertex(self, vertex,objectID):
+        """
+        select a vertex object when adding a segment
+        """
+        self.canvas.delete(objectID)
+        self.selVertex = vertex
+        self.selVertexTag = objectID
+        vertex.draw(self.canvas,objectID, scale =self.SCALE ,colour = VERT_SELECT_ADDING_SEG_COLOR)
+    
+    def deselectVertex(self, vertex,objectID):
+        """
+        deselect a vertex object when adding a segment
+        """
+        self.canvas.delete(objectID)
+        self.selVertex = None
+        self.selVertexTag = None
+        vertex.draw(self.canvas,objectID,  scale =self.SCALE )
+         
+    def drawSegment(self,v1,v2):
+        """
+        Create a seg object, draw it and add it to the mesh data structure
+        """
+        self.serial +=1
+        self.uniqueID = 'M*%d' % self.serial
+        self.userMeshChanged = True
+        seg = self.Segments.draw(v1,v2,self.mesh,self.uniqueID,self.SCALE,self.canvas,None)
+        return seg
+    def printGeoReference(self):
+        try:
+            print "geo reference", self.mesh.geo_reference
+        except:
+            print "no geo reference"
+        
+    def visualiseMesh(self,mesh):
+        """
+        visualise vertices, segments, triangulation, holes
+        """
+        if self.Visualise:
+            for triangle in mesh.getTriangulation():
+                self.serial +=1
+                self.uniqueID = 'M*%d' % self.serial
+                self.Triangles.visualise(triangle,
+                                        self.uniqueID,
+                                        self.canvas,
+                                        self.SCALE)
+
+        if self.Visualise:
+            Triangles = mesh.sets[mesh.setID[self.selSet]]
+            for triangle in Triangles:
+                triangle.draw(self.canvas,1,
+                              scale = self.SCALE,
+                              colour = SET_COLOUR)
+
+        for segment in mesh.getUserSegments():
+            self.serial +=1
+            self.uniqueID = 'M*%d' % self.serial
+            self.Segments.visualise(segment,
+                                    self.uniqueID,
+                                    self.canvas,
+                                    self.SCALE)
+        for vertex in mesh.getUserVertices():
+            self.serial +=1
+            self.uniqueID = 'M*%d' % self.serial
+            self.Vertices.visualise(vertex,
+                                    self.uniqueID,
+                                    self.canvas,
+                                    self.SCALE)
+            
+        for hole in mesh.getHoles():
+            self.serial +=1
+            self.uniqueID = 'M*%d' % self.serial
+            self.Holes.visualise(hole,
+                                    self.uniqueID,
+                                    self.canvas,
+                                    self.SCALE)   
+        for region in mesh.getRegions():
+            self.serial +=1
+            self.uniqueID = 'M*%d' % self.serial
+            self.Regions.visualise(region,
+                                    self.uniqueID,
+                                    self.canvas,
+                                    self.SCALE)
+    def normalise4ObjMesh(self):
+        if self.mesh:
+            self.clearSelections()
+            self.canvas.delete(ALL)
+            self.mesh.normaliseMesh(400,-200,20)
+            self.visualiseMesh(self.mesh)
+            self.ResizeToFit()
+            self.ResizeToFit()
+            
+    def normaliseMesh(self):
+        if self.mesh:
+            self.clearSelections()
+            self.canvas.delete(ALL)
+            self.mesh.normaliseMesh(1,0,1)
+            self.visualiseMesh(self.mesh)
+            self.ResizeToFit()
+            self.ResizeToFit()
+            
+        
+    def clearMesh(self):
+        """Clear the current mesh object, and the canvas """
+       
+        self.clearSelections()
+        self.canvas.delete(ALL)
+        self.deleteMesh()
+        self.initData()
+        self.createMesh()
+
+    def exportObj(self):
+        fileType = "obj"
+        fileTypeDesc = "obj mesh"
+        
+        ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType),
+                                                ("All Files", "*")])
+        if ofile:
+            addOn = "." + fileType
+            jumpback = - len(addOn)
+            if ofile[jumpback:] != addOn:  
+                ofile = ofile + addOn
+            try:
+                self.mesh.exportASCIIobj(ofile)
+            except IOError:
+                showerror('Export ASCII file',
+                                   'Can not write to file.')
+            except RuntimeError:
+                showerror('Export ASCII file',
+                                   'No triangulation to export.')
+
+    
+
+    def ImportUngenerate(self):
+        ofile = tkFileDialog.askopenfilename(initialdir=self.currentPath,
+                                             filetypes=[ ("ungenerated polygon information", "txt"),
+                                           ("All Files", "*")])
+        if ofile == "":
+            # The user cancelled the loading action
+            return
+        
+        try:
+            self.clearSelections()
+            self.canvas.delete(ALL)
+            dict = mesh.importUngenerateFile(ofile)
+            self.mesh.addVertsSegs(dict)
+            
+        except SyntaxError: 
+            #this is assuming that the SyntaxError is thrown in
+            #loadxyafile
+            showerror('File error',
+                      ofile + ' is not in the correct format.')
+        except IOError: 
+            #!!! this error type can not be thrown?
+            showerror('File error',
+                      'file ' + ofile + ' could not be found.')
+        except RuntimeError: 
+            showerror('File error',
+                  'file ' + ofile + ' has an unknown file type.')
+    
+        self.visualiseMesh(self.mesh)
+        self.ResizeToFit()
+        
+    def exportASCIIsegmentoutlinefile(self):
+        
+        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
+                                               filetypes=[("mesh", "*.tsh *.msh"),
+                                             ("All Files", "*")])
+           
+        if ofile:
+            # .tsh is the default file format
+            if (ofile[-4:] == ".tsh" or ofile[-4:] == ".msh"):  
+                self.currentFilePathName = ofile
+            else:
+                self.currentFilePathName = ofile + ".tsh"
+                
+            try:
+                self.mesh.exportASCIIsegmentoutlinefile(ofile)
+            except IOError: #FIXME should this function be throwing any errors?
+                showerror('Export ASCII file',
+                                   'Can not write to file.')
+
+    def exportPointsFile(self):
+        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
+                                         filetypes=[("point files",
+                                                     "*.xya *.pts"),
+                                                ("All Files", "*")])
+        if ofile:
+            # .xya is the default file format
+            if (ofile[-4:] == ".xya" or ofile[-4:] == ".pts"):  
+                self.currentFilePathName = ofile
+            else:
+                self.currentFilePathName = ofile + ".xya"
+                
+            try:
+                self.mesh.exportPointsFile(ofile)
+            except IOError:
+                showerror('Export ASCII file',
+                                   'Can not write to file.')
+                    
+    def importFile(self):
+        """
+        import mesh data from a variety of formats (currently 2!)
+        """
+        print "self.currentPath",self.currentPath
+        ofile = tkFileDialog.askopenfilename(initialdir=self.currentPath,
+                                             filetypes=[ ("text Mesh", "*.tsh *.msh"),
+                                                         ("points", "*.xya *.pts"),
+                                           ("All Files", "*")])
+        if ofile == "":
+            # The user cancelled the loading action
+            return
+        
+        try:
+            newmesh = mesh.importMeshFromFile(ofile)
+            self.currentPath, dummy = os.path.split(ofile)
+            #print "be good self.currentPath",self.currentPath
+            self.currentFilePathName = ofile
+            self.clearMesh()
+            self.mesh = newmesh
+
+            #FIXME - to speed things up, don't visualise the mesh
+            # use ResizeToFitWrapper
+            self.visualiseMesh(self.mesh)
+            self.ResizeToFit()
+       
+        except IOError: 
+            #!!! this error type can not be thrown?
+            showerror('File error',
+                      'file ' + ofile + ' could not be loaded.')
+
+        except RuntimeError: 
+            showerror('File error',
+                  'file ' + ofile + ' has an unknown file type.')
+        # Could not get the file name to showup in the title
+        #appname =  ofile + " - " + APPLICATION_NAME
+        #print "appname",appname
+        
+        except load_mesh.loadASCII.TitleAmountError: 
+            showerror('File error',
+                  'file ' + ofile + ' has a bad title line (first line).')
+
+    
+    def ResizeToFitWrapper(self, Parent):
+        """
+        The parent attribute isn't used by this function.
+        need to understand toolbarbutton.py to know how to
+        get rid of it.
+        """
+        self.ResizeToFit()
+        
+    def ResizeToFit(self):
+        """Visualise the mesh so it fits in the window"""
+        if self.mesh.getUserVertices() == []:
+            return #There are no vertices!
+        # Resize the window
+        self.scrolledcanvas.resizescrollregion()
+        # I need this so the xview values are correct
+        self.scrolledcanvas.update()
+        
+        xtop, xbottom = self.scrolledcanvas.xview()
+        ytop, ybottom = self.scrolledcanvas.yview()
+        xdiff = xbottom-xtop
+        ydiff = ybottom-ytop
+        if xdiff == 1 and xdiff == 1:
+            #The mesh might be too small.
+            #Make it too large, then resize
+            #!!! Recursive hack.  Should be a better way
+            fraction = 50
+            self.SCALE *= fraction
+            self.scrolledcanvas.scale(ALL, 0, 0, fraction, fraction)
+            self.ResizeToFit()
+        else:
+            # without 0.99 some of the mesh may be off screen
+            fraction = 0.99*min(xdiff,ydiff) 
+            self.selectZoom(fraction)
+            
+    def saveDrawing(self):
+        """
+        Save the current drawing
+        """
+        #print "dsg!!! self.currentFilePathName ",self.currentFilePathName
+        if (self.currentFilePathName[-4:] != ".tsh" or
+            self.currentFilePathName[-4:] != ".msh"):
+            # force user to choose a name
+            self.saveAsDrawing()
+        else:
+            self.exportASCIItriangulationfile(self.currentFilePathName)
+
+    def saveAsDrawing(self):
+        """
+        Save the current drawing, prompting for a file name
+        """
+        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
+                                               filetypes=[("mesh", "*.tsh *.msh"),
+                                             ("All Files", "*")])
+           
+        if ofile:
+            # .tsh is the default file format
+            if (ofile[-4:] == ".tsh" or ofile[-4:] == ".msh"):  
+                self.currentFilePathName = ofile
+            else:
+                self.currentFilePathName = ofile + ".tsh"
+            self.exportASCIItriangulationfile(self.currentFilePathName)
+
+    def exportASCIItriangulationfile(self,currentFilePathName):
+        """
+        Have a warning prompt when saving a mesh where the generated mesh is
+        different from the user mesh - eg boundary tags that aren't carried
+        thru. Warning ~"Mesh not generated after changes.  Generate mesh?  "
+        - cancel, don't gen, don't save.  Yes - generate mesh, go to save
+        screen.  No - goto save screen.  To implement this need to know when
+        the user has done a change, and the mesh hasn't been generated.  If
+        there is no generated mesh do not prompt. 
+        """
+        #print "self.UserMeshChanged",self.UserMeshChanged
+        #print "self.mesh.isTriangulation()",self.mesh.isTriangulation()
+        if (self.UserMeshChanged) and self.mesh.isTriangulation():
+            
+            m = _show("Warning",
+                                   "A triangulation has not been generated, after mesh changes.  Generate triangulation before saving?",
+                                   icon=QUESTION, 
+                                   type=YESNOCANCEL)
+            if m == "no":
+                self.mesh.export_mesh_file(currentFilePathName)
+                self.UserMeshChanged = False
+            elif m == "cancel":
+                pass
+            elif m == "yes":
+                self.windowMeshGen(None)
+                self.mesh.export_mesh_file(currentFilePathName)
+        else:
+            self.mesh.export_mesh_file(currentFilePathName)
+            self.UserMeshChanged = False
+            
+    def initData(self):
+        """
+        Initialise various lists and flags
+        """
+        self.serial      = 1000
+        self.currentFilePathName = 'untitled'
+
+        # these are attributes I've added
+        self.SCALE       = 1
+        self.selVertex   = None     #The last vertex selected, in draw seg mode
+        self.selVertexTag   = None     # The selected vertex drawn object tag
+        self.mesh        = None
+        self.MeshMinAngle = 20
+        self.MeshMaxArea = 200
+        self.MeshnumTriangles = 20
+        self.MeshMaxAreaLast = False
+        self.selMeshObject = None  # The mesh object selected in the current mode
+        self.selMeshTag = None
+        mesh.Segment.set_default_tag("")
+        self.UserMeshChanged = False
+        self.meshLastAlpha = None
+        
+        self.Visualise = True #Is the mesh shown or not?
+    
+    def ipostscript(self):
+        """
+        Print the canvas as a postscript file
+        """
+        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
+                                               filetypes=[("postscript", "ps"),
+                                                          ("All Files", "*")]) 
+        if ofile:
+            if ofile[-3:] != ".ps":  
+                ofile = ofile + ".ps"
+            postscript = self.canvas.postscript()
+            fd = open(ofile, 'w')
+            fd.write(postscript)
+            fd.close()
+        
+    def close(self):
+        self.quit()
+        
+    def createInterface(self):
+        """
+        Call all functions that create the GUI interface
+        """
+        self.initData()
+        self.createMesh()
+        AppShell.AppShell.createInterface(self)
+        self.createButtons()
+        self.createMenus()
+        self.createBase()
+        self.createTools()
+        self.createZooms()
+        self.createEdits()
+	self.createSetTools()
+	self.createSetIcons()
+	self.createVisualiseIcons()
+        #self.addCylinders() # !!!DSG start pmesh with a triangle
+        self.selectFunc('pointer')
+        self.currentPath = os.getcwd() 
+
+    def loadtestmesh(self,ofile):
+        """
+        debugging script to test loading a file
+        """
+        fd = open(ofile)
+        a = mesh.Vertex (-10.0, 0.0)
+        d = mesh.Vertex (0.0, 4.0)
+        f = mesh.Vertex (4.0,0.0)
+        g = mesh.Vertex (-5.0,5.0)
+        
+        s1 = mesh.Segment(a,d)
+        s2 = mesh.Segment(d,f)
+        s3 = mesh.Segment(a,f)
+
+        r1 = mesh.Region(0.3, 0.3)
+        
+        m = mesh.Mesh(userVertices=[a,d,f,g], userSegments=[s1,s2,s3], regions=[r1] )
+        
+        fd.close()
+        print 'returning m'
+        return oadtestmesh(ofile)
+         
+class  AddVertexDialog(Dialog):
+    """
+    Dialog box for adding a vertex by entering co-ordindates
+    """
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Add New Vertex")
+        
+        Label(master, text='X position:').grid(row=0, sticky=W)
+        Label(master, text='Y position:').grid(row=1, sticky=W)
+
+        self.xstr   = Entry(master, width = 16, name ="entry")
+        self.ystr  = Entry(master, width = 16)
+       
+        self.xstr.grid(row=0, column=1, sticky=W)
+        self.ystr.grid(row=1, column=1, sticky=W)
+        self.xstr.focus_force()
+        self.x  = 0
+        self.y  = 0
+        self.xyValuesOk = False
+  
+
+    def apply(self):
+        """
+        check entered values
+        """
+        try:
+            self.x = float(self.xstr.get())
+            self.y = float(self.ystr.get())
+            self.xyValuesOk = True
+            
+        except ValueError:
+            showerror('Bad Vertex values',
+                                   'X Y values are not numbers.')
+        
+
+class  AutoSegmentDialog(Dialog):
+    """
+    Dialog box for adding segments
+    """
+    def __init__(self, parent, alpha):
+        self.alpha = alpha
+        Dialog.__init__(self, parent)
+        
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Automatically Add Segments")
+
+        self.use_optimum = IntVar()
+        self.use_optimum.set(AUTO) # should initialise the radio buttons.
+                                   #  It doesn't
+                                   
+        #self.use_optimum.set(NO_SELECTION) 
+        self.ck = Radiobutton(master, value = AUTO, variable=self.use_optimum) 
+        self.ck.grid(row=1, column=0)
+        Label(master, text='Use optimum alpha').grid(row=1, column=1, sticky=W)
+
+        self.ck2 = Radiobutton(master, value = SET_ALPHA,
+                               variable=self.use_optimum) 
+        self.ck2.grid(row=2, column=0)
+        
+        Label(master, text='alpha:').grid(row=2, column=1, sticky=W)
+        if (self.alpha):
+            alphaVar = StringVar()
+            alphaVar.set(self.alpha)
+            self.alpha_str  = Entry(master,
+                                     textvariable = alphaVar,
+                                     width = 16, name ="entry") 
+        else: 
+            self.alpha_str = Entry(master, width = 16, name ="entry")
+        
+        self.alpha_str.grid(row=2, column=3, sticky=W)
+
+        #boundary type buttons
+        self.raw_boundary = IntVar()
+        self.remove_holes = IntVar()
+        self.smooth_indents = IntVar()
+        self.expand_pinch = IntVar()
+        self.ck3 = Checkbutton(master, state=NORMAL,
+                               variable=self.raw_boundary) 
+        self.ck3.grid(row=3, column=0)
+        Label(master, text='Raw boundary').grid(row=3, column=1, sticky=W)
+        #
+        self.ck4 = Checkbutton(master, state=NORMAL,
+                               variable=self.remove_holes) 
+        self.ck4.grid(row=4, column=0)
+        Label(master, text='Remove small holes').grid(row=4,column=1, sticky=W)
+        #
+        self.ck5 = Checkbutton(master,state=NORMAL,
+                               variable=self.smooth_indents) 
+        self.ck5.grid(row=5, column=0)
+        Label(master,
+              text='Remove sharp indents').grid(row=5, column=1, sticky=W)
+        #
+        self.ck6 = Checkbutton(master,state=NORMAL,
+                               variable=self.expand_pinch) 
+        self.ck6.grid(row=6, column=0)
+        Label(master,
+              text='Remove pinch off').grid(row=6, column=1,  sticky=W)
+
+        
+        self.alpha  = 0
+        self.alphaValueOk = False
+        
+
+    def apply(self):
+        """
+        check entered values
+        """
+        try:
+            self.alpha = float(self.alpha_str.get())
+            self.alphaValueOk = True
+            
+        except ValueError:
+            pass
+            #showerror('Bad Alpha value',
+            #                       'Alpha is negative.')
+
+
+class  AutoSegmentFilterDialog(Dialog):
+    """
+    Dialog box for adding segments
+    """
+    def __init__(self, parent):
+        Dialog.__init__(self, parent)
+        
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Automatically Add Segments")
+
+        self.use_optimum = IntVar()
+        self.use_optimum.set(AUTO) # should initialise the radio buttons.
+                                   #  It doesn't
+        self.boundary_type = IntVar()
+                      
+        #boundary type buttons
+        self.raw_boundary = IntVar()
+        self.remove_holes = IntVar()
+        self.smooth_indents = IntVar()
+        self.expand_pinch = IntVar()
+        
+        self.ck3 = Checkbutton(master, state=NORMAL,
+                               variable=self.raw_boundary) 
+        self.ck3.grid(row=3, column=0)
+        Label(master, text='Raw boundary').grid(row=3, column=1, sticky=W)
+        #
+        self.ck4 = Checkbutton(master, state=NORMAL,
+                               variable=self.remove_holes) 
+        self.ck4.grid(row=4, column=0)
+        Label(master, text='Remove small holes').grid(row=4,column=1, sticky=W)
+        #
+        self.ck5 = Checkbutton(master,state=NORMAL,
+                               variable=self.smooth_indents) 
+        self.ck5.grid(row=5, column=0)
+        Label(master,
+              text='Remove sharp indents').grid(row=5, column=1, sticky=W)
+        #
+        self.ck6 = Checkbutton(master,state=NORMAL,
+                               variable=self.expand_pinch) 
+        self.ck6.grid(row=6, column=0)
+        Label(master,
+              text='Remove pinch off').grid(row=6, column=1,  sticky=W)
+        
+        
+
+
+class  MeshGenDialog(Dialog):
+    """
+    Dialog box for generating a mesh
+    """
+    # initial values, hard coded.
+    # should be values associated with the current mesh
+    lastMinAngle = 20
+    lastMaxArea  = 100
+
+
+    def __init__(self,
+                 parent,
+                 minAngle,
+                 maxArea,
+                 numTriangles,
+                 MeshMaxAreaLast):
+        self.minAngle = minAngle
+        self.maxArea = maxArea
+        self.numTriangles = numTriangles
+        self.MeshMaxAreaLast = MeshMaxAreaLast
+
+        Dialog.__init__(self, parent)
+
+        
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Generate Mesh")
+        
+        Label(master,
+              text='Minimum Angle(0 - 40):').grid(row=0, sticky=W)
+        Label(master,
+              text='Angles>33 may not converge').grid(row=1, sticky=W)
+        Label(master, text='Maximum Area:').grid(row=2, sticky=W)
+        Label(master, text='OR # of triangles:').grid(row=3, sticky=W)
+
+
+        minAngleVar = StringVar()
+        minAngleVar.set(self.minAngle)
+        self.minAnglestr   = Entry(master,
+                                   width = 16,
+                                   textvariable = minAngleVar,
+                                   takefocus=1)
+        if (self.MeshMaxAreaLast):
+            maxAreaVar = StringVar()
+            maxAreaVar.set(self.maxArea)
+            self.maxAreastr  = Entry(master,
+                                     textvariable = maxAreaVar,
+                                     width = 16)    
+            self.numTrianglesstr  = Entry(master,
+                                          width = 16) 
+        else: 
+            self.maxAreastr  = Entry(master,
+                                     width = 16)
+            self.maxAreastr.focus_force()
+            numTrianglesVar = StringVar()
+            numTrianglesVar.set(self.numTriangles)    
+            self.numTrianglesstr  = Entry(master,
+                                          textvariable = numTrianglesVar,
+                                          width = 16) 
+
+
+        self.minAnglestr.grid(row=0, column=1, sticky=W)
+        self.maxAreastr.grid(row=2, column=1, sticky=W)
+        self.numTrianglesstr.grid(row=3, column=1, sticky=W)
+
+        self.numTriangles = 0
+        self.minAngle  = 0
+        self.maxArea  = 0
+        self.ValuesOk = False
+
+
+    def apply(self):
+        """
+        check entered values
+        """
+        self.goodMaxArea = self.goodNumTriangles = True
+        self.ValuesOk = True
+        try:
+            self.minAngle = float(self.minAnglestr.get())
+            MeshGenDialog.lastMinAngle =self.minAngle
+        except ValueError:
+            self.ValuesOk = False
+            showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+        
+        try:    
+            self.maxArea = float(self.maxAreastr.get())
+            MeshGenDialog.lastMaxArea =self.maxArea
+        except ValueError:
+            self.goodMaxArea = False
+         
+        try:    
+            self.numTriangles = int(self.numTrianglesstr.get())
+            MeshGenDialog.lastNumTriangles =self.numTriangles
+        except ValueError:
+            self.goodNumTriangles= False
+
+        if self.goodMaxArea == False and self.goodNumTriangles == False:
+            self.ValuesOk = False
+            showerror('Bad mesh generation values',
+                      'Values are not numbers.')
+
+        if self.goodMaxArea == True and self.goodNumTriangles == True:
+            self.ValuesOk = False
+            showerror('Bad mesh generation values',
+                      'Give a maximum area OR number of triangles, not both.')
+
+        try: 
+            # value checking
+            if self.minAngle <0.0 or self.minAngle >40.0:
+                raise IOError
+            if self.goodMaxArea == True and self.maxArea <0.0:
+                raise IOError
+            if self.goodNumTriangles == True and self.numTriangles <=0:
+                raise IOError
+            
+        except IOError:
+            self.ValuesOk = False
+            showerror('Bad mesh generation values',
+                                   'Values are out of range.')
+class  GeneralThresholdDialog(Dialog):
+    """
+    Dialog box for thresholding a set by entering minimum 
+    and maximum values
+    """
+    def __init__(self,
+                 parent,
+                 attribute_titles,
+                 function_description):
+        self.attribute_titles=attribute_titles
+        self.function_description=function_description
+
+        Dialog.__init__(self, parent)
+
+
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Threshold selected set")
+        blurb1 = 'Threshold selected set between minimum'
+        blurb2 = 'and maximum ' + self.function_description
+
+        Label(master,text=blurb1).grid(row=0, sticky=W)
+        Label(master,text=blurb2).grid(row=1, sticky=W)
+
+        Label(master, text='minimum attribute:').grid(row=2, sticky=W)
+        Label(master, text='maximum attribute:').grid(row=3, sticky=W)
+        Label(master, text='attribute name').grid(row=4, sticky=W)
+
+
+        nameVar = StringVar()
+        nameVar.set('elevation')
+
+        self.minstr = Entry(master, width = 16, name ="entry")
+        self.maxstr = Entry(master, width = 16)
+        self.attstr = Entry(master, width = 16,textvariable = nameVar)
+       
+        self.minstr.grid(row=2, column=1, sticky=W)
+        self.maxstr.grid(row=3, column=1, sticky=W)
+        self.attstr.grid(row=4, column=1, sticky=W)
+        self.minstr.focus_force()
+        self.min  = 0
+        self.max  = 0
+        self.attribute_name = 'elevation'
+        self.minmaxValuesOk = False
+        
+    def apply(self):
+        self.minmaxValuesOk = True
+        try:
+            self.min = float(self.minstr.get())
+            self.max = float(self.maxstr.get())
+        except ValueError:
+            self.minmaxValuesOk = False
+            showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+        try:
+            self.attribute_titles.index(self.attstr.get())#dodgey.
+            self.attribute_name = self.attstr.get()
+        except ValueError:
+            self.attribute_name = None
+            showerror('Bad attribute name',
+                                   'Using h = 1')
+
+class  ThresholdDialog(Dialog):
+    """
+    Dialog box for thresholding a set by entering minimum 
+    and maximum values
+    """
+    def __init__(self,
+                 parent,
+                 attribute_titles):
+        self.attribute_titles=attribute_titles
+        Dialog.__init__(self, parent)
+
+
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Threshold selected set")
+        
+        Label(master, text='minimum attribute:').grid(row=0, sticky=W)
+        Label(master, text='maximum attribute:').grid(row=1, sticky=W)
+        Label(master, text='attribute name').grid(row=2, sticky=W)
+
+
+        nameVar = StringVar()
+        nameVar.set('elevation')
+
+        self.minstr   = Entry(master, width = 16, name ="entry")
+        self.maxstr   = Entry(master, width = 16)
+        self.attstr   = Entry(master, width = 16,textvariable = nameVar)
+       
+        self.minstr.grid(row=0, column=1, sticky=W)
+        self.maxstr.grid(row=1, column=1, sticky=W)
+        self.attstr.grid(row=2, column=1, sticky=W)
+        self.minstr.focus_force()
+        self.min  = 0
+        self.max  = 0
+        self.attribute_name = 'elevation'
+        self.minmaxValuesOk = False
+        
+    def apply(self):
+        self.minmaxValuesOk = True
+        try:
+            self.min = float(self.minstr.get())
+            self.max = float(self.maxstr.get())
+        except ValueError:
+            self.minmaxValuesOk = False
+            showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+        try:
+            self.attribute_titles.index(self.attstr.get())#dodgey.
+            self.attribute_name = self.attstr.get()
+        except ValueError:
+            self.minmaxValuesOk = False
+            showerror('Bad attribute name',
+                                   ' Attribute not in mesh.')
+
+
+class  Courant_ThresholdDialog(Dialog):
+    """
+    Dialog box for thresholding a set by entering minimum 
+    and maximum values
+    """
+    def __init__(self,
+                 parent,
+                 attribute_titles):
+        self.attribute_titles=attribute_titles
+        Dialog.__init__(self, parent)
+
+
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Courant_Threshold selected set")
+        
+        Label(master, text='minimum attribute:').grid(row=0, sticky=W)
+        Label(master, text='maximum attribute:').grid(row=1, sticky=W)
+        Label(master, text='attribute name').grid(row=2, sticky=W)
+
+
+        nameVar = StringVar()
+        nameVar.set('elevation')
+
+        self.minstr   = Entry(master, width = 16, name ="entry")
+        self.maxstr   = Entry(master, width = 16)
+        self.attstr   = Entry(master, width = 16,textvariable = nameVar)
+       
+        self.minstr.grid(row=0, column=1, sticky=W)
+        self.maxstr.grid(row=1, column=1, sticky=W)
+        self.attstr.grid(row=2, column=1, sticky=W)
+        self.minstr.focus_force()
+        self.min  = 0
+        self.max  = 0
+        self.attribute_name = 'elevation'
+        self.minmaxValuesOk = False
+        
+    def apply(self):
+        self.minmaxValuesOk = True
+        try:
+            self.min = float(self.minstr.get())
+            self.max = float(self.maxstr.get())
+        except ValueError:
+            self.minmaxValuesOk = False
+            showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+        try:
+            self.attribute_titles.index(self.attstr.get())#dodgey.
+            self.attribute_name = self.attstr.get()
+        except ValueError:
+            self.minmaxValuesOk = False
+            showerror('Bad attribute name',
+                                   ' Attribute not in mesh.')
+
+
+class SmoothDialog(Dialog):
+    """
+    Dialog box for setting the number of triangles
+    used to make up dilation or erosion
+    """
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Enter the minimum radius to remove")
+        
+        Label(master, text='radius:').grid(row=0, sticky=W)
+
+        self.min_radius = Entry(master, width = 16, name ="entry")
+       
+        self.min_radius.grid(row=0, column=1, sticky=W)
+        self.min_radius.focus_force()
+        self.min = 2.
+        self.valueOK = False
+        
+    def apply(self):
+        self.valueOK = True
+        try:
+            self.min = float(self.min_radius.get())
+            self.min_radius = self.min
+        except ValueError:
+            self.valueOK = False
+            showerror('Bad Number',
+                                   ' Value not a number')
+
+class  setStructureNumberDialog(Dialog):
+    """
+    Dialog box for setting the number of triangles
+    used to make up dilation or erosion
+    """
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Set number of elements effected by morphing sets")
+        
+        Label(master, text='number:').grid(row=0, sticky=W)
+
+        self.number = Entry(master, width = 16, name ="entry")
+       
+        self.number.grid(row=0, column=1, sticky=W)
+        self.number.focus_force()
+        self.number = 0
+        self.numberOk = False
+        
+    def apply(self):
+        self.numberOk = True
+        try:
+            self.number = int(self.number.get())
+        except ValueError:
+            self.numberOK = False
+            showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+
+
+      
+if __name__ == '__main__':
+    draw = Draw()
+    draw.run()
+    #profile.run('draw.run()', 'pmeshprof')    
+
Index: /inundation/pmesh/scripts/create_gong.py
===================================================================
--- /inundation/pmesh/scripts/create_gong.py	(revision 1719)
+++ /inundation/pmesh/scripts/create_gong.py	(revision 1719)
@@ -0,0 +1,66 @@
+
+import os, sys
+sys.path.append('..')
+
+#problem, it's loading pyvolution mesh 1st!
+from mesh import *
+from coordinate_transforms.geo_reference import Geo_reference
+
+#-------------------------------------------------------------
+if __name__ == "__main__":
+
+    # from 100m dem
+    geo = Geo_reference(xllcorner = 241191.35087962,
+                        yllcorner = 6111741.8729311,
+                        zone = 56)
+    m = Mesh(geo_reference=geo)
+    bellambi_northing = 6195000
+    north_beach_northing = 6183000
+    border = 12000
+    west = 301000 # From the map 
+    inner_east = 310000 # From the map 
+    inner_west_south = 303000 # From the map 
+    inner_west_north = 307000  # From the map 
+    east = 328421
+    center = (east+inner_east)/2
+    
+    dict = {}
+    dict['pointlist'] = [[west, north_beach_northing - border],  #sw
+                         [west, bellambi_northing + border],  #nw
+                         [center,
+                          bellambi_northing + border],  #n_center
+                         [east, bellambi_northing + border], #ne
+                         [east, north_beach_northing - border],    #se
+                         [center,
+                          north_beach_northing - border]    #s_center
+                         ]
+    dict['segmentlist'] = [[0,1],[1,2],[2,3],
+                           [3,4],[4,5],[5,0], # the outer boarder
+                           [2,5]   # the center line
+                           ]
+    
+    m.addVertsSegs(dict)
+
+    dict = {}
+    dict['pointlist'] = [
+                         [inner_west_south,north_beach_northing], #sw
+                         [inner_west_north,bellambi_northing],   #nw
+                         [inner_east,bellambi_northing], #ne
+                         [inner_east,north_beach_northing] #se
+                         ]
+    dict['segmentlist'] = [[0,1],[1,2],[2,3],[3,0]] # the inner boarder
+    m.addVertsSegs(dict)
+
+    factor = 10000 #low res 10000, high res 1000
+    low = m.addRegionEN(east-0.5, bellambi_northing + border-0.5)
+    low.setMaxArea(100*factor)
+    
+    medium = m.addRegionEN(center-0.5, bellambi_northing + border-0.5)
+    medium.setMaxArea(10*factor)
+    
+    high = m.addRegionEN(inner_east-0.5,bellambi_northing-0.5)
+    high.setMaxArea(1*factor)
+    m.generateMesh()
+    
+    m.export_triangulation_file("wollongong_outline.msh")
+    
Index: /inundation/pmesh/scripts/csv2xya.py
===================================================================
--- /inundation/pmesh/scripts/csv2xya.py	(revision 1719)
+++ /inundation/pmesh/scripts/csv2xya.py	(revision 1719)
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+"""
+Convert a .cvs file from gis to a xya file, for pmesh.
+(removes the 1st column in a cvs file and renames it from .cvs to .xya)
+"""
+import os, sys
+
+usage = "usage: %s input_file_name.csv" %         os.path.basename(sys.argv[0])
+
+if (len(sys.argv)< 2) or not (sys.argv[1][-4:]== ".csv"):
+    print usage
+else:
+    read_file_name = sys.argv[1]
+    write_file_name = read_file_name[:-4] + ".xya"
+    file_obj = open(read_file_name)
+    lines = file_obj.readlines()
+    file_obj.close()
+
+    
+    fd = open(write_file_name,'w')
+    for line in lines:
+        elements = line.split(',')
+        elements.pop(0) # remove the 1st column
+        if len(elements) > 0: #if there is a 2nd column, write it
+            fd.write(elements.pop(0))
+        for element in elements:
+            fd.write(',')
+            fd.write(element)
Index: /inundation/pmesh/scripts/thin_out_vertices.py
===================================================================
--- /inundation/pmesh/scripts/thin_out_vertices.py	(revision 1719)
+++ /inundation/pmesh/scripts/thin_out_vertices.py	(revision 1719)
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+"""
+Script to thin out the number of vertices in an xya file.
+Put's a square mesh over the domain and then only keeps
+one vertice in each square.
+"""
+import os, sys
+sys.path.append('..')
+from mesh import *
+
+usage = "usage: %s file_name square_length" %         os.path.basename(sys.argv[0])
+
+if len(sys.argv) < 3:
+    print usage
+else:
+    file_name_path= sys.argv[1]
+    square_length= float(sys.argv[2])
+    wallis_name = file_name_path[:-4] + "_" + str(square_length) + ".xya"
+    m = loadxyafile(file_name_path," ")
+    m.thinoutVertices(square_length)
+    print "Saving file " + wallis_name + "."
+    m.exportxyafile(wallis_name)
Index: /inundation/pmesh/setup.py
===================================================================
--- /inundation/pmesh/setup.py	(revision 1719)
+++ /inundation/pmesh/setup.py	(revision 1719)
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+setup (name = "pMesh",
+       version = "1.XX",
+       author="Duncan Gray",
+       author_email="duncan.gray@ga.gov.au",
+       ext_modules = [
+                      Extension("triang",["triangle/triangle.c",
+                                          "triangle/triangmodule.c"],
+                                define_macros=[("TRILIBRARY",1),
+                                               ("NO_TIMER",1)]
+                                )           
+                      
+                      ]
+       )
Index: /inundation/pmesh/test_mesh.py
===================================================================
--- /inundation/pmesh/test_mesh.py	(revision 1719)
+++ /inundation/pmesh/test_mesh.py	(revision 1719)
@@ -0,0 +1,1612 @@
+#!/usr/bin/env python
+#
+import tempfile
+import unittest
+from mesh import *
+from load_mesh.loadASCII import *
+from coordinate_transforms.geo_reference import Geo_reference
+
+class meshTestCase(unittest.TestCase):
+    def setUp(self):
+        pass
+        
+
+
+    def tearDown(self):
+        pass
+
+    def testPointDistance(self):
+        a = Point(0.0, 0.0)
+        b = Point(0.0, 10.0)
+        
+        self.failUnless( a.DistanceToPoint(b) == 10.0,
+                        'Point DistanceToPoint is wrong!')
+    
+    def testVertexDistance(self):
+        a = Vertex (0.0, 0.0)
+        b = Vertex (0.0, 10.0)
+        
+        self.failUnless( a.DistanceToPoint(b) == 10.0,
+                        'Point DistanceToPoint is wrong!')
+        
+    def testTriangle(self):
+        a = Vertex (0.0, 0.0)
+        b = Vertex (0.0, 2.0)
+        c = Vertex (2.0,0.0)
+        d = Vertex (0.0, 4.0)
+        e = Vertex (2.0, 2.0)
+        f = Vertex (4.0,0.0)
+        
+        t1 = Triangle(b,a,c)       
+        t2 = Triangle(b,c,e)      
+        t3 = Triangle(e,c,f)      
+        t4 = Triangle(d,b,e)
+        t2.setNeighbors(t3,t4,t1)
+        
+        self.failUnless( t2.neighbors[2].vertices[0] == b, 'Triangle initialisation is wrong!')
+    
+        
+    def testSegment(self):
+        a = Vertex (0.0, 0.0)
+        b = Vertex (0.0, 10.0)
+        s = Segment(a,b, tag = 20)     
+        
+        self.failUnless( s.vertices[0].DistanceToPoint(s.vertices[1]) == 10.0,
+                        'vertices in a segment are wrong')
+        
+        self.failUnless( s.tag == 20.0,
+                        'tag in a segment are wrong')
+
+    def testdeleteUserVertex(self):
+
+        
+        mesh = Mesh()
+        a = mesh.addUserVertex(0.0, 0.0)
+        b = mesh.addUserVertex (0.0, 2.0)
+        c = mesh.addUserVertex (2.0,0.0)
+        
+        s1 = mesh.addUserSegment(a,b)
+        s2 = mesh.addUserSegment(a,c)
+        s3 = mesh.addUserSegment(c,b)
+
+        mesh.deleteMeshObject (a) 
+        self.failUnless(mesh.userSegments[0] == s3,
+                        'Bad segment. ')        
+        self.failUnless(len(mesh.userSegments) ==1,
+                        'Segments not deleted.')
+        self.failUnless(len(mesh.userVertices) == 2,
+                        'Vertex not deleted.')
+       
+  
+        
+    def testgenerateMesh(self):
+        a = Vertex (0.0, 0.0)
+        d = Vertex (0.0, 4.0)
+        f = Vertex (4.0,0.0)
+
+        s1 = Segment(a,d)
+        s2 = Segment(d,f)
+        s3 = Segment(a,f)
+
+        r1 = Region(0.3, 0.3,tag = 1.3,maxArea = .6)
+        #print r1
+        m = Mesh(userVertices=[a,d,f], userSegments=[s1,s2,s3], regions=[r1] )
+        
+        m.generateMesh("QApz", maxArea = 2.1 )         
+
+        #print m
+
+        #m.plotMeshTriangle()
+
+        result = 1.414214
+        delta  = 0.00001
+        
+        self.failUnless((m.meshTriangles[1].vertices[0].x < result + delta) or
+                        (m.meshTriangles[1].vertices[0].x > result - delta),
+                        'generated mesh is wrong!')
+
+    def test_regionalMaxArea(self):
+        v0 = Vertex (0.0, 0.0)
+        v1 = Vertex (6.0, 0.0)
+        v2 = Vertex (6.0,6.0)
+        v3 = Vertex (0.0,6.0)
+
+        s1 = Segment(v0,v1)
+        s2 = Segment(v1,v2)
+        s3 = Segment(v3,v2)
+        s4 = Segment(v3,v0)
+        s5 = Segment(v2,v0)
+
+        r1 = Region(3, 1,tag = 1.3)
+        #print r1
+        m = Mesh(userVertices=[v0,v1,v2,v3], userSegments=[s1,s2,s3,s4,s5], regions=[r1] )
+        
+        m.generateMesh("Apz", maxArea = 36 )         
+
+        #m.plotMeshTriangle()
+        #print "len(m.meshTriangles)",len(m.meshTriangles)
+
+        self.failUnless(len(m.meshTriangles) == 2, 
+                        'test_regionalMaxArea 1:generated mesh is wrong!')
+        
+        ## Another test case
+        r1 = Region(3, 1,tag = 1.3)
+        r2 = Region(1, 3,tag = 1.3, maxArea = 8)
+        m = Mesh(userVertices=[v0,v1,v2,v3], userSegments=[s1,s2,s3,s4,s5],
+                 regions=[r1,r2] )
+        m.generateMesh("Apz", maxArea = 36 )         
+        self.failUnless(len(m.meshTriangles) == 6, 
+                        'test_regionalMaxArea 2:generated mesh is wrong!')
+        
+        ## Another test case
+        r1 = Region(3, 1, tag = 1.3, maxArea = 8)
+        r2 = Region(1, 3, tag = 1.3, maxArea = 8)
+        m = Mesh(userVertices=[v0,v1,v2,v3], userSegments=[s1,s2,s3,s4,s5],
+                 regions=[r1,r2] )
+        m.generateMesh("Apz", maxArea = 36 )  
+        #print "len(m.meshTriangles)",len(m.meshTriangles)       
+        self.failUnless(len(m.meshTriangles) == 8, 
+                        'test_regionalMaxArea 3:generated mesh is wrong!')
+        
+        ## Another test case
+        r1 = Region(3, 1, tag = 1.3 )
+        r2 = Region(1, 3, tag = 1.3, maxArea = 8)
+        m = Mesh(userVertices=[v0,v1,v2,v3], userSegments=[s1,s2,s3,s4,s5],
+                 regions=[r1,r2] )
+        m.generateMesh("Apz", maxArea = 8 )         
+        self.failUnless(len(m.meshTriangles) == 8, 
+                        'test_regionalMaxArea 4:generated mesh is wrong!')
+        
+        ## Another test case
+        r1 = Region(3, 1,tag = 1.3, maxArea = 8)
+        r2 = Region(1, 3,tag = 1.3, maxArea = 8)
+        m = Mesh(userVertices=[v0,v1,v2,v3], userSegments=[s1,s2,s3,s4,s5],
+                 regions=[r1,r2] )
+        m.generateMesh("Apz", maxArea = 36,isRegionalMaxAreas = False )      
+        self.failUnless(len(m.meshTriangles) == 2, 
+                        'test_regionalMaxArea 5:generated mesh is wrong!')
+        
+    def testdeleteUserVertex(self):
+        mesh = Mesh()
+        a = mesh.addUserVertex(0.0, 0.0)
+        b = mesh.addUserVertex (0.0, 2.0)
+        c = mesh.addUserVertex (2.0,0.0)
+        
+        s1 = mesh.addUserSegment(a,b)
+        s2 = mesh.addUserSegment(a,c)
+        s3 = mesh.addUserSegment(c,b)
+
+        mesh.deleteMeshObject (s2)
+        
+        #print ",s2 in mesh.userSegments" ,s2 in mesh.userSegments
+        self.failUnless(not(s2 in mesh.userSegments),
+                        'Bad segment. ')        
+        self.failUnless(len(mesh.userSegments) ==2,
+                        'Segments not deleted.')
+        self.failUnless(len(mesh.userVertices) == 3,
+                        'Vertex deleted, instead of segment.')
+
+    def testTriangleArea(self):
+        a = Vertex (10.0, 10.0)
+        b = Vertex (10.0, 20.0)
+        c = Vertex (20.0,10.0)
+        
+        d = Vertex (-20.0, 0.0)
+        e = Vertex (-20.0, -20.0)
+        f = Vertex (20.0,-20.0)
+        
+        t1 = Triangle(b,a,c)      
+        t2 = Triangle(e,d,f)
+        
+#         print "t1", t1
+#         print "t1 area ", t1.calcArea()
+#         print "t2", t2
+#         print "t2 area ", t2.calcArea()
+        self.failUnless( t1.calcArea() == 50 and t2.calcArea() == 400, 'Triangle area is wrong!')
+    def testisUserSegmentNew (self):
+        mesh = Mesh()
+        a = mesh.addUserVertex(0.0, 0.0)
+        b = mesh.addUserVertex (0.0, 2.0)
+        c = mesh.addUserVertex (2.0,0.0)
+        d = mesh.addUserVertex (2.0,3.0)
+        
+        s1 = mesh.addUserSegment(a,b)
+        s2 = mesh.addUserSegment(a,c)
+        s3 = mesh.addUserSegment(c,b)
+
+        self.failUnless(mesh.isUserSegmentNew(a,d) ,
+                        'Segment should be new. ')
+        self.failUnless(not(mesh.isUserSegmentNew(a,b)) ,
+                        'Segment should not be new. ')
+
+
+    def testisUserSegmentNew (self):
+        mesh = Mesh()
+        a = mesh.addUserVertex(0.0, 0.0)
+        b = mesh.addUserVertex (0.0, 2.0)
+        c = mesh.addUserVertex (2.0,0.0)
+        d = mesh.addUserVertex (2.0,3.0)
+        
+        s1 = mesh.addUserSegment(a,b)
+        s2 = mesh.addUserSegment(a,c)
+        s3 = mesh.addUserSegment(c,b)
+
+        self.failUnless(mesh.representedUserSegment(a,d) == None,
+                        'Segment should be new. ')
+        self.failUnless(mesh.representedUserSegment(a,b) == s1 ,
+                        'Segment should not be new. ')
+        
+    def testautoSegment(self):
+        p0 = Vertex (0.0, 0.0)
+        p1 = Vertex (0.0, 4.0)
+        p2 = Vertex (4.0,4.0)
+        p3 = Vertex (4.0,0.0)
+
+        s1 = Segment(p0,p1)
+        
+        m = Mesh(userVertices=[p0, p1, p2, p3], userSegments=[s1] ) 
+        m.autoSegment()
+        
+        #print 'Len', len(m.userSegments)
+        self.failUnless(len(m.getUserSegments()) == 4 ,
+                        'userSegments is wrong!')
+     
+        m.autoSegment()
+        self.failUnless(len(m.getUserSegments()) == 4 ,
+                        'userSegments is wrong!')
+     
+    def testautoSegmentII(self):
+        p1 = Vertex (3.0, 4.0)
+        p2 = Vertex (3.0,2.0)
+        p3 = Vertex (3.0,0.0)
+        p4 = Vertex (6.0, 4.0)
+        p5 = Vertex (6.0,2.0)
+        p0 = Vertex (6.0,0.0)
+
+
+        s1 = Segment(p2,p3)
+        s2 = Segment(p4,p5)
+        
+        m = Mesh(userVertices=[p0, p1, p2, p3, p4, p5],
+                 userSegments=[s1, s2])     
+
+        m.autoSegment()
+        
+        s3 = m.representedAlphaUserSegment(p3,p0)
+        self.failUnless(not (s3 == None) ,
+                        'userSegments is wrong!')
+
+        
+        s6 = m.representedAlphaUserSegment(p1,p4)       
+        self.failUnless(not (s6 == None) ,
+                        'userSegments is wrong!')
+        
+        # remove a segment, add a point, autosegment
+        m.alphaUserSegments.remove(s3)
+        p6 = Vertex (1.0, 2.0)
+        m.userVertices.append(p6)
+        
+        m.autoSegment()
+        
+        s1_now = m.representedUserSegment(p3,p2)
+        self.failUnless(s1_now == s1 ,
+                        'userSegments is wrong!')
+        
+        s2_now = m.representedUserSegment(p5,p4)       
+        self.failUnless(s2_now == s2 ,
+                        'userSegments is wrong!')
+        
+        s3 = m.representedAlphaUserSegment(p3,p6)       
+        self.failUnless(not (s3 == None) ,
+                        'userSegments is wrong!')
+        
+        s4 = m.representedAlphaUserSegment(p3,p6)       
+        self.failUnless(not (s4 == None) ,
+                        'userSegments is wrong!')
+        
+        s5 = m.representedAlphaUserSegment(p4,p6)       
+        self.failUnless(s5 == None ,
+                        'userSegments is wrong!')
+        #print m
+        
+    def testRegions(self):
+        a = Vertex (0.0, 0.0)
+        b = Vertex (0.0, 2.0)
+        c = Vertex (2.0,0.0)
+        d = Vertex (0.0, 4.0)
+        e = Vertex (2.0, 2.0)
+        f = Vertex (4.0,0.0)
+        g = Vertex (0.0,-2.0)
+        
+        Segment.set_default_tag("")
+        s1 = Segment(a,b)
+        s2 = Segment(b,e)
+        s3 = Segment(e,c)
+        s4 = Segment(c,a)
+        s5 = Segment(b,d)
+        s6 = Segment(e,d)
+        s7 = Segment(e,f)
+        s8 = Segment(c,f)
+        s9 = Segment(g,c)
+        s10 = Segment(g,a)
+
+        r1 = Region(0.1,0.1,tag="22")
+        r2 = Region(0.1,2.1,tag="11")
+        r3 = Region(2.1,0.1)
+        
+        m = Mesh(userVertices=[a,b,c,d,e,f,g], userSegments=[s1,s2,s3,s4,s5,s6,s7,s8,s9,s10], regions=[r1,r2,r3] )
+        m.generateMesh("QApz", maxArea = 2.1 )
+        #print m
+        Triangulation =  m.getTriangulation()
+        #print Triangulation[0].attribute
+        #print Triangulation[1].attribute 
+        #print Triangulation[2].attribute 
+        #print Triangulation[3].attribute 
+        #print Triangulation[4].attribute
+       
+        self.failUnless(Triangulation[0].attribute == "" and
+                        Triangulation[1].attribute == "22" and
+                        Triangulation[2].attribute == "" and
+                        Triangulation[3].attribute == "11" and
+                        Triangulation[4].attribute == "22" ,
+                        'region attributes are wrong!')   
+
+    def test_vertexAttribs(self):
+        a = Vertex (0.0, 0.0, attributes = [12.0,2.0])
+        d = Vertex (0.0, 4.0, attributes = [9.0,7.0])
+        f = Vertex (4.0,0.0, attributes = [14.0,3.0])
+    
+        Segment.set_default_tag("")
+        s1 = Segment(a,d)
+        s2 = Segment(d,f)
+        s3 = Segment(a,f)
+     
+        r1 = Region(0.3, 0.3, tag = 88.9)
+     
+        m = Mesh(userVertices=[a,d,f], userSegments=[s1,s2,s3], regions=[r1])
+
+        m.generateMesh("QApz", maxArea = 2.1)
+
+        vert = m.getMeshVertices()
+        
+        self.failUnless(vert[0].attributes == [12.0, 2.0] and
+                        vert[1].attributes == [9.0, 7.0] and
+                        vert[2].attributes == [14.0,3.0] and
+                        vert[3].attributes == [12.232233047033631, 4.4142135623730949] and
+                        vert[4].attributes == [13.0, 2.5] ,
+                        'vertex attributes are wrong!')
+
+        
+    def test_vertexAttribs2(self):
+    
+        a = Vertex (0.0, 0.0)
+        d = Vertex (0.0, 4.0)
+        f = Vertex (4.0,0.0)
+    
+        Segment.set_default_tag("")
+        s1 = Segment(a,d)
+        s2 = Segment(d,f)
+        s3 = Segment(a,f)
+     
+        r1 = Region(0.3, 0.3, tag = 88.9)
+     
+        m = Mesh(userVertices=[a,d,f], userSegments=[s1,s2,s3], regions=[r1])
+
+        m.generateMesh("QApz", maxArea = 2.1 )
+
+        vert = m.getMeshVertices()
+        self.failUnless(vert[0].attributes == [] and
+                        vert[1].attributes == [] and
+                        vert[2].attributes == [] and
+                        vert[3].attributes == [] and
+                        vert[4].attributes == [],
+                        'vertex attributes are wrong!')
+
+    def test_segtag(self):
+    
+        a = Vertex (0.0, 0.0)
+        d = Vertex (0.0, 4.0)
+        f = Vertex (4.0,0.0)
+    
+        s1 = Segment(a,d,tag = 5)
+        s2 = Segment(d,f,tag = 7)
+        s3 = Segment(a,f,tag = 9)
+     
+        m = Mesh(userVertices=[a,d,f], userSegments=[s1,s2,s3])
+
+        m.generateMesh("QApz", maxArea = 2.1 )
+
+        seg = m.getMeshSegments()
+        
+        #print "seg[0].tag"
+        #print seg[0].tag
+        #print "seg[0].tag"
+        self.failUnless(seg[0].tag == 5 and
+                        seg[1].tag == 7 and
+                        seg[2].tag == 9 and
+                        seg[3].tag == 7 and
+                        seg[4].tag == 9 and
+                        seg[5].tag == 7 and
+                        seg[6].tag == 5,
+                        'seg tags are wrong')
+
+    def test_segtag2(self):
+    
+        a = Vertex (0.0, 0.0)
+        d = Vertex (0.0, 4.0)
+        f = Vertex (4.0,0.0)
+        e = Vertex (1.0,1.0)
+    
+        s1 = Segment(a,d)
+        s2 = Segment(d,f)
+        s3 = Segment(a,f)
+        s4 = Segment(a,e)
+     
+        m = Mesh(userVertices=[a,d,f,e], userSegments=[s1,s2,s3,s4])
+
+        m.generateMesh("QApz", maxArea = 2.1)
+
+        seg = m.getMeshSegments()
+        self.failUnless(seg[0].tag == "exterior" and
+                        seg[1].tag == "exterior" and
+                        seg[2].tag == "exterior" and
+                        seg[3].tag == "" and
+                        seg[4].tag == "exterior",
+                        '2nd seg tags are wrong')
+
+    def test_asciiFile(self):
+    
+        a = Vertex (0.0, 0.0)  #, attributes = [1.1])
+        d = Vertex (0.0, 4.0)  #, attributes = [1.2])
+        f = Vertex (4.0,0.0)  #, attributes = [1.3])
+        e = Vertex (1.0,1.0)  #, attributes = [1.4])
+    
+        s1 = Segment(a,d)
+        s2 = Segment(d,f)
+        s3 = Segment(a,f)
+        s4 = Segment(a,e)
+     
+        m = Mesh(userVertices=[a,d,f,e], userSegments=[s1,s2,s3,s4])
+
+        m.generateMesh("QApz", maxArea = 2.1 )
+
+        seg = m.getMeshSegments()
+        
+        fileName = tempfile.mktemp(".tsh")
+        m.export_mesh_file(fileName)
+        file = open(fileName)
+        lFile = file.read().split('\n')
+        file.close()
+        os.remove(fileName)
+        
+        #print "@^@^"
+        #for l in lFile:
+        #    print l,"<"
+        #print "@^@^"
+
+        # no need to check the title again
+        #self.failUnless(lFile[0] == "5 0 # <vertex #> <x> <y> [attributes] ...Triangulation Vertices..."
+          #              ,'Ascii file is wrong, vertex title')
+        self.failUnless(lFile[1] == "0 0.0 0.0 " and #1.1 " and
+                        lFile[2] == "1 0.0 4.0 " and #1.2 " and
+                        lFile[3] == "2 4.0 0.0 " and #1.3 " and
+                        lFile[4] == "3 1.0 1.0 " and #1.4 " and
+                        lFile[5] == "4 2.0 2.0 "  #1.25 " 
+                        ,
+                        'Ascii file is wrong, vertex')
+        
+        #self.failUnless(lFile[6] == "# attribute column titles ...Triangulation Vertex Titles..."
+          #              ,'Ascii file is wrong, attribute column title')
+        self.failUnless(lFile[8] == "0 3 2 4 -1 2 3  " and
+                        lFile[9] == "1 1 0 3 3 2 -1  " and
+                        lFile[10] == "2 3 4 1 -1 1 0  " and
+                        lFile[11] == "3 2 3 0 1 -1 0  "
+                        ,
+                        'Ascii file is wrong, triangle') 
+
+        self.failUnless( lFile[13] == "0 0 1 exterior" and
+                        lFile[14] == "1 1 4 exterior" and
+                        lFile[15] == "2 2 0 exterior" and
+                        lFile[16] == "3 0 3 " and
+                        lFile[17] == "4 4 2 exterior" ,
+                        'Ascii file is wrong, segment')
+        
+       # self.failUnless(lFile[18] == '4 0 # <vertex #> <x> <y> [attributes] ...Mesh Vertices...',
+        #                'Ascii file is wrong, Mesh Vertices Title')
+        
+        self.failUnless(lFile[19] == '0 0.0 0.0 ' and #1.1 ' and
+                        lFile[20] == '1 0.0 4.0 ' and #1.2 ' and
+                        lFile[21] == '2 4.0 0.0 ' and #1.3 ' and
+                        lFile[22] == '3 1.0 1.0 ' #1.4 '
+                        ,
+                        'Ascii file is wrong, Mesh Vertices II')
+        
+        self.failUnless(lFile[24] == '0 0 1 ' and
+                        lFile[25] == '1 1 2 ' and
+                        lFile[26] == '2 0 2 ' and
+                        lFile[27] == '3 0 3 '
+                        ,'Ascii file is wrong, Mesh Segments')       
+
+  
+    def test_ascii_file(self):
+    
+        a = Vertex (0.0, 0.0) #, attributes = [1.1])
+        d = Vertex (0.0, 4.0) #, attributes = [1.2])
+        f = Vertex (4.0,0.0) #, attributes = [1.3])
+        e = Vertex (1.0,1.0) #, attributes = [1.4])
+    
+        s1 = Segment(a,d)
+        s2 = Segment(d,f)
+        s3 = Segment(a,f)
+        s4 = Segment(a,e)
+     
+        m = Mesh(userVertices=[a,d,f,e], userSegments=[s1,s2,s3,s4])
+
+        m.generateMesh("QApz", maxArea = 2.1 )
+
+        seg = m.getMeshSegments()
+        
+        fileName = tempfile.mktemp(".tsh")
+        m.export_mesh_file(fileName)
+        file = open(fileName)
+        lFile = file.read().split('\n')
+        file.close()
+        os.remove(fileName)
+        
+        #print "@^@^"
+        #for l in lFile:
+        #    print l,"<"
+        #print "@^@^"
+        self.failUnless(lFile[0] == "5 0 # <# of verts> <# of vert attributes>, next lines <vertex #> <x> <y> [attributes] ...Triangulation Vertices..."
+                        ,
+                        'Ascii file is wrong, vertex title')
+        self.failUnless(lFile[1] == "0 0.0 0.0 " and #1.1 " and
+                        lFile[2] == "1 0.0 4.0 " and #1.2 " and
+                        lFile[3] == "2 4.0 0.0 " and #1.3 " and
+                        lFile[4] == "3 1.0 1.0 " and #1.4 " and
+                        lFile[5] == "4 2.0 2.0 "  #1.25 " 
+                        ,
+                        'Ascii file is wrong, vertex')
+        
+        self.failUnless(lFile[6] == "# attribute column titles ...Triangulation Vertex Titles..."
+                        ,
+                        'Ascii file is wrong, attribute column title')
+        self.failUnless(lFile[7] == "4 # <# of triangles>, next lines <triangle #> [<vertex #>] [<neigbouring triangle #>] [attribute of region] ...Triangulation Triangles..." and
+                        lFile[8] == "0 3 2 4 -1 2 3  " and
+                        lFile[9] == "1 1 0 3 3 2 -1  " and
+                        lFile[10] == "2 3 4 1 -1 1 0  " and
+                        lFile[11] == "3 2 3 0 1 -1 0  "
+                        ,
+                        'Ascii file is wrong, triangle') 
+
+        self.failUnless(lFile[12] == "5 # <# of segments>, next lines <segment #> <vertex #>  <vertex #> [boundary tag] ...Triangulation Segments..." and
+                        lFile[13] == "0 0 1 exterior" and
+                        lFile[14] == "1 1 4 exterior" and
+                        lFile[15] == "2 2 0 exterior" and
+                        lFile[16] == "3 0 3 " and
+                        lFile[17] == "4 4 2 exterior" ,
+                        'Ascii file is wrong, segment')
+        
+        self.failUnless(lFile[18] == '4 0 # <# of verts> <# of vert attributes>, next lines <vertex #> <x> <y> [attributes] ...Mesh Vertices...',
+                        'Ascii file is wrong, Mesh Vertices Title')
+        
+        self.failUnless(lFile[19] == '0 0.0 0.0 ' and #1.1 ' and
+                        lFile[20] == '1 0.0 4.0 ' and #1.2 ' and
+                        lFile[21] == '2 4.0 0.0 ' and #1.3 ' and
+                        lFile[22] == '3 1.0 1.0 ' #1.4 '
+                        ,
+                        'Ascii file is wrong, Mesh Vertices II')
+        
+        self.failUnless(lFile[23] == '4 # <# of segments>, next lines <segment #> <vertex #>  <vertex #> [boundary tag] ...Mesh Segments...' and
+                        lFile[24] == '0 0 1 ' and
+                        lFile[25] == '1 1 2 ' and
+                        lFile[26] == '2 0 2 ' and
+                        lFile[27] == '3 0 3 ' and
+                        lFile[28] == '0 # <# of holes>, next lines <Hole #> <x> <y> ...Mesh Holes...' and
+                        lFile[29] == '0 # <# of regions>, next lines <Region #> <x> <y> <tag>...Mesh Regions...'
+                        ,
+                        'Ascii file is wrong, Mesh Segments')       
+  
+
+    def test_thinoutVertices(self):
+
+        v1 = Vertex(-20,-20)
+        v2 = Vertex(-11,-11)
+        v3 = Vertex(-10,-10)
+        v4 = Vertex(-9,-1)
+        v5 = Vertex(-8,2)
+        v6 = Vertex(6,3)
+        v7 = Vertex(12,9)
+        v8 = Vertex(15,3)
+        v9 = Vertex(24,3)
+        m = Mesh(userVertices = [v1,v2,v3,v4,v5,v6,v7,v8,v9])
+        m.thinoutVertices(10)
+         
+        self.failUnless(v1 in m.userVertices,
+                        'test_thinoutVertices, test 1 failed')
+        self.failUnless(v3 in m.userVertices,
+                        'test_thinoutVertices, test 2 failed')
+        self.failUnless(v4 in m.userVertices,
+                        'test_thinoutVertices, test 3 failed')
+        self.failUnless(v6 in m.userVertices,
+                        'test_thinoutVertices, test 4 failed')
+        self.failUnless(v7 in m.userVertices,
+                        'test_thinoutVertices, test 5 failed')
+        self.failUnless(v9 in m.userVertices,
+                        'test_thinoutVertices, test 6 failed')
+        self.failUnless(v5 not in m.userVertices,
+                        'test_thinoutVertices, test 7 failed')
+        self.failUnless(v2 not in m.userVertices,
+                        'test_thinoutVertices, test 8 failed')
+        self.failUnless(v8 not in m.userVertices,
+                        'test_thinoutVertices, test 9 failed')
+
+    def test_same_x_y(self):
+        v = Point(7,8)
+        f = Point(7,8)
+        f.same_x_y(v)
+
+        self.failUnless(f.same_x_y(v),
+                        'same_x_y True failed')
+        e = Point(7,9)
+        self.failUnless(not f.same_x_y(e),
+                        'same_x_y False failed')
+
+    def test_import_tsh(self):
+        
+        a_att = [5.0,2.0]
+        d_att =[4.0,2.0]
+        f_att = [3.0,2.0]
+        e_att = [2.0,2.0]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1]) #, attributes =a_att)
+        d = Vertex (0.0, 4.0) #, attributes =d_att)
+        f = Vertex (4.0,0.0) #, attributes =f_att)
+        e = Vertex (1.0,1.0) #, attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3")
+        geo = Geo_reference(8.9,8.9,65)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1],
+                 geo_reference=geo)
+
+        m.generateMesh("QApz", maxArea = 2.1)
+        fileName = tempfile.mktemp(".tsh")
+        #print "dgs!!!"
+        #print "****************** fileName", fileName
+        m.export_mesh_file(fileName)
+        #print "******************"
+        #print "m", m
+        #print "******************"
+        m_returned = importMeshFromFile(fileName)
+        #print "m_returned",m_returned
+        #print "******************"
+        #print "****************** fileName", fileName
+        os.remove(fileName)
+        self.failUnless(0 == m.__cmp__(m_returned),
+                        'loading and saving of a mesh failed')
+        # do this when .msh supports geo_refs
+        #self.failUnless(m.geo_reference == m_returned.geo_reference,
+        #                'loading and saving of a mesh geo refs failed')
+
+    def test_import_mesh(self):
+        
+        a_att = [5.0,2.0]
+        d_att =[4.0,2.0]
+        f_att = [3.0,2.0]
+        e_att = [2.0,2.0]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1]) #, attributes =a_att)
+        d = Vertex (0.0, 4.0) #, attributes =d_att)
+        f = Vertex (4.0,0.0) #, attributes =f_att)
+        e = Vertex (1.0,1.0) #, attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3")
+        geo = Geo_reference(65,8.9,8.9)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1],
+                 geo_reference=geo)
+
+        m.generateMesh("QApz", maxArea = 2.1)
+        fileName = tempfile.mktemp(".msh")
+        #print "dgs!!!"
+        #print "****************** fileName", fileName
+        m.export_mesh_file(fileName)
+        #print "******************"
+        #print "m", m
+        #print "******************"
+        m_returned = importMeshFromFile(fileName)
+        #print "m_returned",m_returned
+        #print "******************"
+        #print "****************** fileName", fileName
+        os.remove(fileName)
+        #print "m.geo_reference",m.geo_reference
+        #print "m_returned.geo_reference,",m_returned.geo_reference
+        self.failUnless(0 == m.__cmp__(m_returned),
+                        'loading and saving of a mesh failed')
+        self.failUnless(m.geo_reference == m_returned.geo_reference,
+                        'loading and saving of a mesh geo refs failed')
+
+    def test_normaliseMesh(self):
+        
+        a_att = [5.0,2.0]
+        d_att =[4.0,2.0]
+        f_att = [3.0,2.0]
+        e_att = [2.0,2.0]
+        a_xy = [10.0, 10.0]
+        a = Vertex ( a_xy[0],a_xy[1], attributes =a_att)
+        d = Vertex (15.0, 10.0, attributes =d_att)
+        f = Vertex (10.0,20.0, attributes =f_att)
+        e = Vertex (15.0,20.0, attributes =e_att)
+    
+        s1 = Segment(a,d, tag = 50)
+        s2 = Segment(d,e, tag = 40)
+        s3 = Segment(e,f, tag = 30)
+        s4 = Segment(f,a, tag = 20)
+     
+        r1 = Region(0.3, 0.3,tag = 1.3)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1])
+        m.normaliseMesh(1,0,1)
+        [xmin, ymin, xmax, ymax] = m.boxsize()
+        [attmin, attmax] = m.maxMinVertAtt(0)
+        self.failUnless(attmin == 0.0 and attmax == 1.0,
+                        'normalise failed')
+        self.failUnless(xmin == 0.0 and ymin == 0.0 and xmax == 0.5 and ymax == 1.0,
+                        'normalise failed')
+        m.normaliseMesh(200,-100,5)
+        [xmin, ymin, xmax, ymax] = m.boxsize()
+        [attmin, attmax] = m.maxMinVertAtt(0)
+        self.failUnless(attmin == 0.0 and attmax == 5.0,
+                        'normalise failed')
+        self.failUnless(xmin == -100.0 and ymin == -100.0 and xmax == 0.0 and ymax == 100.0,
+                        'normalise failed')
+        
+    def test_exportASCIIsegmentoutlinefile(self):
+        a = Vertex (0,0)
+        b = Vertex (0,3)
+        c = Vertex (3,3)
+        d = Vertex (1,2)
+        e = Vertex (3,1)
+      
+        s1 = Segment(a,b, tag = "50")
+        s2 = Segment(b,c, tag = "40")
+        s3 = Segment(c,a, tag = "30")
+     
+        r1 = Region(2, 1,tag = "1.3")
+        h1 = Hole(1,4)
+        m = Mesh(userVertices=[a,b,c,d,e],
+                 userSegments=[s1,s2,s3],
+                 regions=[r1],
+                 holes = [h1])      
+        
+        m.generateMesh("QApz", maxArea = 2.1)
+        #print "mesh ***************dsg*", m
+
+        fileName = tempfile.mktemp(".tsh")
+        m.exportASCIIsegmentoutlinefile(fileName)
+        
+        m_returned = importMeshFromFile(fileName)
+
+        #print "m_returned ****",m_returned
+        #print "****************** fileName", fileName
+        os.remove(fileName)
+
+        #Trim mesh, so it should like like m_returned
+        m.meshVertices = []
+        m.meshTriangles = []
+        m.meshSegments = []
+        m.userVertices=[a,b,c]
+        #print "mesh ***************dsg*", m
+        #print "(m.__cmp__(m_returned)", m.__cmp__(m_returned) 
+        self.failUnless(0 == m.__cmp__(m),
+                        'test_exportASCIIsegmentoutlinefile:loading and saving of a mesh failed')
+        self.failUnless(0 == m.__cmp__(m_returned),
+                        'test_exportASCIIsegmentoutlinefile:loading and saving of a mesh failed')
+ 
+    def test_exportASCIIsegmentoutlinefile2(self):
+        a = Vertex (0,0)
+        b = Vertex (0,1)
+        c = Vertex (1,0)
+        d = Vertex (1,1)
+        e = Vertex (0.5,0.5)
+        f  = Vertex (0.6,0.6)
+      
+        s1 = Segment(a,e, tag = "50")
+        s2 = Segment(b,e, tag = "40")
+        s3 = Segment(c,e, tag = "30")
+        s4 = Segment(d,e, tag = "30")
+     
+        r1 = Region(2, 1,tag = "1.3")
+        h1 = Hole(1,4)
+        m = Mesh(userVertices=[a,b,c,d,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1],
+                 holes = [h1])      
+        
+        fileName = tempfile.mktemp(".tsh")
+        m.exportASCIIsegmentoutlinefile(fileName)
+        
+        m_returned = importMeshFromFile(fileName)
+        #print "****************** fileName", fileName
+        os.remove(fileName)
+
+        #Trim mesh, so it should like like m_returned
+        m.meshVertices = []
+        m.meshTriangles = []
+        m.meshSegments = []
+        m.userVertices=[a,e,d,b,c]
+        #print "mesh ***************dsg*", m
+        #print "(m.__cmp__(m_returned)", m.__cmp__(m_returned) 
+        self.failUnless(0 == m.__cmp__(m),
+                        'test_exportASCIIsegmentoutlinefile:loading and saving of a mesh failed')
+        self.failUnless(0 == m.__cmp__(m_returned),
+                        'test_exportASCIIsegmentoutlinefile:loading and saving of a mesh failed')
+
+    def test_loadxy(self):
+        """
+        To test the mesh side of loading xya files.
+        Not the loading of xya files
+        """
+        import os
+        import tempfile
+       
+        fileName = tempfile.mktemp(".xya")
+        file = open(fileName,"w")
+        file.write("elevation speed \n\
+1.0 0.0 10.0 0.0\n\
+0.0 1.0 0.0 10.0\n\
+1.0 0.0 10.4 40.0\n")
+        file.close()
+        #print fileName
+        m = importMeshFromFile(fileName)
+        os.remove(fileName)
+        self.failUnless(m.userVertices[0].x == 1.0,
+                        'loadxy, test 1 failed')
+        self.failUnless(m.userVertices[0].y == 0.0,
+                        'loadxy, test 2 failed')
+        #self.failUnless(m.userVertices[0].attributes == [10.0,0.0],
+        #                'loadxy, test 2.2 failed')
+        self.failUnless(m.userVertices[1].x == 0.0,
+                        'loadxy, test 3 failed')
+        self.failUnless(m.userVertices[1].y == 1.0,
+                        'loadxy, test 4 failed')
+        #self.failUnless(m.userVertices[1].attributes == [0.0,10.0],
+        #                'loadxy, test 5 failed')
+        
+    def test_exportPointsFile(self):
+        a = Vertex (0,0)
+        b = Vertex (0,3)
+        c = Vertex (3,3)
+        d = Vertex (1,2)
+        e = Vertex (3,1)
+        f = Vertex (3,1)
+      
+        s1 = Segment(a,b, tag = 50)
+        s2 = Segment(b,c, tag = 40)
+        s3 = Segment(c,a, tag = 30)
+     
+        r1 = Region(2, 1,tag = 1.3)
+        h1 = Hole(1,4)
+        # Warning mesh can't produce this type of data structure its self
+        m = Mesh(userVertices=[a,b,c,d,e],
+                 userSegments=[s1,s2,s3],
+                 regions=[r1],
+                 holes = [h1])
+        
+        fileName = tempfile.mktemp(".xya")
+        m.exportPointsFile(fileName)
+        file = open(fileName)
+        lFile = file.read().split('\n')
+        file.close()
+
+        os.remove(fileName)
+        self.failUnless(lFile[0] == "" and
+                        lFile[1] == "0,0" and
+                        lFile[2] == "0,3" and
+                        lFile[3] == "3,3" and
+                        lFile[4] == "1,2" and
+                        lFile[5] == "3,1" 
+                        ,
+                        'exported Ascii xya file is wrong')
+       
+        m.generateMesh("QApz", maxArea = 2.1)
+        fileName = tempfile.mktemp(".xya")
+        m.exportPointsFile(fileName)
+        file = open(fileName)
+        lFile = file.read().split('\n')
+        file.close()
+        os.remove(fileName)
+        self.failUnless(lFile[0] == "" and
+                        lFile[1] == "0.0,0.0" and
+                        lFile[2] == "0.0,3.0" and
+                        lFile[3] == "3.0,3.0" and
+                        lFile[4] == "1.0,2.0" and
+                        lFile[5] == "3.0,1.0" and
+                        lFile[6] == "1.5,1.5"  
+                        ,
+                        'exported Ascii xya file is wrong')
+           
+    def test_exportPointsFilefile2(self):
+        m = Mesh()
+        
+        fileName = tempfile.mktemp(".xya")
+        m.exportPointsFile(fileName)
+        file = open(fileName)
+        lFile = file.read().split('\n')
+        file.close()
+
+        os.remove(fileName)
+        #print "************* test_mesh exportPointsFilefile"
+        #print "lFile",lFile 
+        #print "************* test_mesh exportPointsFilefile"
+        self.failUnless(lFile[0] == "" 
+                        ,
+                        'exported Ascii xya file is wrong')
+        
+    def test_strings2ints(self):
+        list = ["sea","river inlet","","sea","","moat"]
+        preset = ["moat", "internal boundary"]
+        [intlist, converter] = segment_strings2ints(list,preset )
+        self.failUnless(intlist == [2,3 ,0 ,2 ,0 ,0 ]
+                        ,
+                        'test_strings2ints produces bad intlist')
+        self.failUnless(converter == ['moat', 'internal boundary',
+                                      'sea', 'river inlet']
+                        ,
+                        'test_strings2ints produces bad converter')
+        
+    def test_ints2strings(self):
+        list = ["internal boundary","sea","river inlet",
+            "","sea","","moat","internal boundary"]
+        outlist = ['internal boundary', 'sea', 'river inlet', 'moat',
+                   'sea', 'moat', 'moat', 'internal boundary']
+        preset = ["moat", "internal boundary"]
+        [intlist, converter] = segment_strings2ints(list,preset )
+        newlist = segment_ints2strings(intlist, converter)
+        self.failUnless(outlist == newlist
+                        ,
+                        'test_strings2ints produces bad intlist')
+        self.failUnless(converter == ['moat', 'internal boundary',
+                                      'sea', 'river inlet']
+                        ,
+                        'test_strings2ints produces bad converter')
+        
+    def test_ints2strings2(self):
+        list = ["","",""]
+        preset = ["moat", "internal boundary"]
+        [intlist, converter] = segment_strings2ints(list,preset )
+        newlist = segment_ints2strings(intlist, converter)
+        outlist = ['moat', 'moat', 'moat']
+        self.failUnless(outlist == newlist
+                        ,
+                        'test_strings2ints produces bad intlist')
+        self.failUnless(converter == ['moat', 'internal boundary']
+                        ,
+                        'test_strings2ints produces bad converter')
+
+        
+    def test_removeDuplicatedVertices(self):
+        a = Vertex (0,0)
+        a.index = 0
+        b = Vertex (0,3)
+        b.index = 1
+        c = Vertex (3,3)
+        c.index = 2
+        d = Vertex (1,1)
+        d.index = 3
+        e = Vertex (3,1)
+        e.index = 4
+        f = Vertex (1,1)
+        f.index = 5
+        g = Vertex (1,1)
+        g.index = 6
+        inputVerts_noDups = [a,b,c,d,e]
+        
+        m = Mesh(userVertices=[a,b,c,d,e,f,g])
+        counter = m.removeDuplicatedUserVertices()
+        UserVerts = m.getUserVertices()
+        
+         
+        self.failUnless(UserVerts == inputVerts_noDups,
+                            'duplicate verts not removed')
+        #for userVert, inputVert in map(None, UserVerts, inputVerts_noDups): 
+        #    self.failUnless(userVert.x == inputVert.x,
+        #                    'x duplicate verts not removed')
+        #    self.failUnless(userVert.y == inputVert.y,
+        #                    'y duplicate verts not removed')
+
+        
+    def test_ungenerateFileLoading(self):
+        
+        fileName = tempfile.mktemp(".txt")
+        file = open(fileName,"w")
+        file.write("         1       ??      ??\n\
+       0.0       0.0\n\
+       1.0       0.0\n\
+       1.0       1.0\n\
+       0.0       1.0\n\
+       0.0       0.0\n\
+END\n\
+         2      ?? ??\n\
+       10.0       10.0\n\
+       10.0       20.0\n\
+       20.0       20.0\n\
+       10.0       10.0\n\
+END\n\
+END\n")
+        file.close()
+        
+        
+        a = Vertex (0.0, 0.0) #, attributes = [1.1])
+        b = Vertex (0.0, 40.0) #, attributes = [1.2])
+        c = Vertex (40.0,40.0) #, attributes = [1.3])
+        d = Vertex (40.0,0.0) #, attributes = [1.4])
+    
+        s1 = Segment(a,b)
+        s2 = Segment(b,c)
+        s3 = Segment(c,d)
+        s4 = Segment(d,a)
+     
+        m = Mesh(userVertices=[a,b,c,d], userSegments=[s1,s2,s3,s4])
+        dict = importUngenerateFile(fileName)
+        os.remove(fileName)
+
+        tag = "DSG"
+        Segment.set_default_tag(tag)
+        m.addVertsSegs(dict)
+
+        # have to reset this , since it's a class attribute
+        Segment.set_default_tag("")
+            
+        self.failUnless(len(m.userSegments) ==11,
+                        'Wrong segment list length.')
+        self.failUnless(len(m.userVertices) == 11,
+                        'Wrong vertex list length.')
+        self.failUnless(m.userSegments[10].vertices[0] == m.userVertices[10],
+                        'bad vertex on segment.')
+        self.failUnless(m.userSegments[10].vertices[1] == m.userVertices[8],
+                        'Bad segment.')
+        self.failUnless(m.userSegments[10].tag == tag,
+                        'wrong tag.')
+   
+    def test_ungenerateFileLoadingII(self):
+        
+        fileName = tempfile.mktemp(".txt")
+        file = open(fileName,"w")
+        file.write("         1       ??      ??\n\
+       0.0       0.0\n\
+       1.0       0.0\n\
+       1.0       1.0\n\
+       0.0       1.0\n\
+       0.0       0.0\n\
+END\n\
+         2      ?? ??\n\
+       10.0       10.0\n\
+       10.0       20.0\n\
+       20.0       20.0\n\
+END\n\
+END\n")
+        file.close()
+        
+        
+        a = Vertex (0.0, 0.0) #, attributes = [1.1])
+        b = Vertex (0.0, 40.0) #, attributes = [1.2])
+        c = Vertex (40.0,40.0) #, attributes = [1.3])
+        d = Vertex (40.0,0.0) #, attributes = [1.4])
+    
+        s1 = Segment(a,b)
+        s2 = Segment(b,c)
+        s3 = Segment(c,d)
+        s4 = Segment(d,a)
+     
+        m = Mesh(userVertices=[a,b,c,d], userSegments=[s1,s2,s3,s4])
+        dict = importUngenerateFile(fileName)
+        os.remove(fileName)
+
+        tag = "DSG"
+        Segment.set_default_tag(tag)
+        m.addVertsSegs(dict)
+
+        self.failUnless(len(m.userSegments) ==10,
+                        'Wrong segment list length.')
+        self.failUnless(len(m.userVertices) == 11,
+                        'Wrong vertex list length.')
+        
+    def test_addVertsSegs(self):
+        m = Mesh()
+        Segment.set_default_tag("food")
+        dict = {}
+        dict['points'] = [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]
+        dict['segments'] = [[0, 1], [1, 2]]
+        dict['segment_tags'] = ['','do-op']
+        m.addVertsSegs(dict)
+        # have to reset this , since it's a class attribute
+        Segment.set_default_tag("")
+
+        
+        self.failUnless(len(m.userSegments) ==2,
+                        'Wrong segment list length.')
+        self.failUnless(len(m.userVertices) == 3,
+                        'Wrong vertex list length.')
+        self.failUnless(m.userSegments[0].tag =='food',
+                        'Wrong segment tag length.')
+        self.failUnless(m.userSegments[1].tag =='do-op',
+                        'Wrong segment tag.')
+        
+    def test_exportASCIImeshfile(self):
+    
+        #a_att = [5,2]
+        #d_att =[4,2]
+        #f_att = [3,2]
+        #e_att = [2,2]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1]) #, attributes =a_att)
+        d = Vertex (0.0, 4.0) #, attributes =d_att)
+        f = Vertex (4.0,0.0) #, attributes =f_att)
+        e = Vertex (1.0,1.0) #, attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3", maxArea = 36)
+
+
+        h1 = Hole(0.2,0.6)
+        
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1],
+                 holes=[h1])
+
+        seg = m.getUserSegments()
+        points = m.getUserVertices()
+        holes = m.getHoles()
+        regions = m.getRegions()
+        fileName = tempfile.mktemp(".tsh")
+        m.export_mesh_file(fileName)
+        #print "***************************fileName", fileName
+        new_m = importMeshFromFile(fileName)
+        os.remove(fileName)
+        
+
+        #print '**@@@@@******'
+        #print "new_m",new_m 
+        #print '**@@@@@******'
+        #print "m",m 
+        #print '**@@@@@******'
+        
+        self.failUnless( new_m == m,
+                         'loadASCIITestCase failed. test new 1')
+            
+    def test_Mesh2MeshList(self):
+
+        a_att = [5,2]
+        d_att =[4,2]
+        f_att = [3,2]
+        e_att = [2,2]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1]) #, attributes =a_att)
+        d = Vertex (0.0, 4.0) #, attributes =d_att)
+        f = Vertex (4.0,0.0) #, attributes =f_att)
+        e = Vertex (1.0,1.0) #, attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3", maxArea = 45)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1])
+
+        m.generateMesh("QApza2.1")
+
+        seg = m.getMeshSegments()
+        points = m.getMeshVertices()
+        dict = m.Mesh2MeshList()
+        #print "dict",dict 
+        # test not finished...
+  
+    def test_Mesh2IOTriangulationDict(self):
+
+        a_att = [5,2]
+        d_att =[4,2]
+        f_att = [3,2]
+        e_att = [2,2]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1] , attributes =a_att)
+        d = Vertex (0.0, 4.0 , attributes =d_att)
+        f = Vertex (4.0,0.0 , attributes =f_att)
+        e = Vertex (1.0,1.0 , attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3", maxArea = 45)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1])
+        titles = ['ele','friction']
+        m.attributeTitles = titles
+        m.generateMesh("QApza2.1")
+
+        seg = m.getMeshSegments()
+        verts = m.getMeshVertices()
+        dict = m.Mesh2IOTriangulationDict()
+        #print "dict",dict 
+        
+        self.failUnless( dict['vertex_attribute_titles'] == titles,
+                         'test_Mesh2IOTriangulationDict failed. test 1')
+        answer = [a_xy,[0.0, 4.0],[4.0,0.0], [1.0,1.0], [2.0,2.0]]
+        #print "answer",answer
+        #print "dict['vertices']",dict['vertices']
+        
+        self.failUnless( dict['vertices'] == answer,
+                         'test_Mesh2IOTriangulationDict failed. test 2')
+
+        
+        for pimport,pactual,pimpatt in map(None,dict['vertices'],
+                                           verts,dict['vertex_attributes']):
+            #assert all_close( pimport, (pactual.x,pactual.y))
+            self.failUnless( pimport == [pactual.x,pactual.y],
+                        'test_Mesh2IOTriangulationDict failed. test 2.1')
+            self.failUnless( pimpatt == pactual.attributes,
+                        'test_Mesh2IOTriangulationDict failed. test 2.2')
+        self.failUnless( dict['segments'][0] == [0,1],
+                        'test_Mesh2IOTriangulationDict failed. test 3')
+        for segimp,segactual in map(None,dict['segment_tags'],seg):
+            self.failUnless( segimp == segactual.tag,
+                        'test_Mesh2IOTriangulationDict failed. test 4')
+        #print " dict['triangles'][0]", dict['triangles'][0] 
+        self.failUnless( dict['triangles'][0] == [3,2,4],
+                        'test_Mesh2IOTriangulationDict failed. test 5')
+        self.failUnless( dict['triangle_neighbors'][0] == [-1,2,3],
+                        'test_Mesh2IOTriangulationDict failed. test 6')
+        self.failUnless( dict['triangle_tags'][0] == "1.3",
+                         'test_Mesh2IOTriangulationDict failed. test 7')
+
+  
+    def test_Mesh2IODict(self):
+
+        a_att = [5,2]
+        d_att =[4,2]
+        f_att = [3,2]
+        e_att = [2,2]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1] , attributes =a_att)
+        d = Vertex (0.0, 4.0 , attributes =d_att)
+        f = Vertex (4.0,0.0 , attributes =f_att)
+        e = Vertex (1.0,1.0 , attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3", maxArea = 45)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1])
+        titles = ['ele','friction']
+        m.attributeTitles = titles
+        m.generateMesh("QApza2.1")
+
+        seg = m.getMeshSegments()
+        verts = m.getMeshVertices()
+        dict = m.Mesh2IODict()
+        #print "dict",dict 
+        
+        self.failUnless( dict['vertex_attribute_titles'] == titles,
+                         'test_Mesh2IOTriangulationDict failed. test 1')
+        answer = [a_xy,[0.0, 4.0],[4.0,0.0], [1.0,1.0], [2.0,2.0]]
+        #print "answer",answer
+        #print "dict['vertices']",dict['vertices']
+        
+        self.failUnless( dict['vertices'] == answer,
+                         'test_Mesh2IOTriangulationDict failed. test 2')
+
+        
+        for pimport,pactual,pimpatt in map(None,dict['vertices'],
+                                           verts,dict['vertex_attributes']):
+            #assert all_close( pimport, (pactual.x,pactual.y))
+            self.failUnless( pimport == [pactual.x,pactual.y],
+                        'test_Mesh2IODict failed. test 2.1')
+            self.failUnless( pimpatt == pactual.attributes,
+                        'test_Mesh2IODict failed. test 2.2')
+        self.failUnless( dict['segments'][0] == [0,1],
+                        'test_Mesh2IODict failed. test 3')
+        for segimp,segactual in map(None,dict['segment_tags'],seg):
+            self.failUnless( segimp == segactual.tag,
+                        'test_Mesh2IODict failed. test 4')
+        #print " dict['triangles'][0]", dict['triangles'][0] 
+        self.failUnless( dict['triangles'][0] == [3,2,4],
+                        'test_Mesh2IODict failed. test 5')
+        self.failUnless( dict['triangle_neighbors'][0] == [-1,2,3],
+                        'test_Mesh2IODict failed. test 6')
+        self.failUnless( dict['triangle_tags'][0] == "1.3",
+                         'test_Mesh2IODict failed. test 7')
+
+        seg = m.getUserSegments()
+        points = m.getUserVertices()
+        holes = m.getHoles()
+        regions = m.getRegions()
+        
+        for pimport,pactual,pimpatt in map(None,dict['points'],points,dict['point_attributes']):
+            self.failUnless( pimport == [pactual.x,pactual.y],
+                        'test_Mesh2IODict failed. test 1')
+            self.failUnless( pimpatt == pactual.attributes,
+                        'test_Mesh2IODict failed. test 1.1')
+        self.failUnless( dict['outline_segments'][0] == [0,1],
+                        'test_Mesh2IODict failed. test 3')
+        for segimp,segactual in map(None,dict['outline_segment_tags'],seg):
+            self.failUnless( segimp == segactual.tag,
+                        'test_Mesh2IODict failed. test 4')
+        for holeimp,holeactual in map(None,dict['holes'],holes):
+            self.failUnless( holeimp == [holeactual.x,holeactual.y],
+                        'test_Mesh2IODict failed. test 5')
+        
+        for regimp,regactual,regattimp, regmaxarea in map(None,dict['regions'],regions, dict['region_tags'], dict['region_max_areas']):
+            self.failUnless( regimp == [regactual.x,regactual.y],
+                        'loadASCIITestCase failed. test 6')
+            self.failUnless( regattimp == regactual.getTag(),
+                        'loadASCIITestCase failed. test 7')
+            self.failUnless( regmaxarea == regactual.getMaxArea(),
+                        'loadASCIITestCase failed. test 7')
+    
+            
+        
+    def test_Mesh2IOOutlineDict(self):
+
+        a_att = [5,2]
+        d_att =[4,2]
+        f_att = [3,2]
+        e_att = [2,2]
+        a_xy = [0.0, 0.0]
+        a = Vertex ( a_xy[0],a_xy[1] , attributes =a_att)
+        d = Vertex (0.0, 4.0 , attributes =d_att)
+        f = Vertex (4.0,0.0 , attributes =f_att)
+        e = Vertex (1.0,1.0 , attributes =e_att)
+    
+        s1 = Segment(a,d, tag = "50")
+        s2 = Segment(d,f, tag = "40")
+        s3 = Segment(a,f, tag = "30")
+        s4 = Segment(a,e, tag = "20")
+     
+        r1 = Region(0.3, 0.3,tag = "1.3", maxArea = 45)
+        m = Mesh(userVertices=[a,d,f,e],
+                 userSegments=[s1,s2,s3,s4],
+                 regions=[r1])
+        titles = ['ele','friction']
+        m.attributeTitles = titles
+        m.generateMesh("QApza2.1")
+
+        seg = m.getMeshSegments()
+        verts = m.getMeshVertices()
+        dict = m.Mesh2IOOutlineDict()
+        
+        seg = m.getUserSegments()
+        points = m.getUserVertices()
+        holes = m.getHoles()
+        regions = m.getRegions()
+        
+        for pimport,pactual,pimpatt in map(None,dict['points'],points,dict['point_attributes']):
+            self.failUnless( pimport == [pactual.x,pactual.y],
+                        'loadASCIITestCase failed. test 1')
+            self.failUnless( pimpatt == pactual.attributes,
+                        'loadASCIITestCase failed. test 1.1')
+        self.failUnless( dict['outline_segments'][0] == [0,1],
+                        'loadASCIITestCase failed. test 3')
+        for segimp,segactual in map(None,dict['outline_segment_tags'],seg):
+            self.failUnless( segimp == segactual.tag,
+                        'loadASCIITestCase failed. test 4')
+        for holeimp,holeactual in map(None,dict['holes'],holes):
+            self.failUnless( holeimp == [holeactual.x,holeactual.y],
+                        'loadASCIITestCase failed. test 5')
+        #for regimp,regactual in map(None,dict['regions'],regions):
+         #   self.failUnless( [regimp[0],regimp[1]]==[regactual.x,regactual.y],
+          #              'loadASCIITestCase failed. test 6')
+           # self.failUnless( regimp[2] == regactual.getTag(),
+            #            'loadASCIITestCase failed. test 7')
+            #self.failUnless( regimp[3] == regactual.getMaxArea(),
+             #           'loadASCIITestCase failed. test 7')
+
+            
+        for regimp,regactual,regattimp, regmaxarea in map(None,dict['regions'],regions, dict['region_tags'], dict['region_max_areas']):
+            self.failUnless( regimp == [regactual.x,regactual.y],
+                        'loadASCIITestCase failed. test 6')
+            self.failUnless( regattimp == regactual.getTag(),
+                        'loadASCIITestCase failed. test 7')
+            self.failUnless( regmaxarea == regactual.getMaxArea(),
+                        'loadASCIITestCase failed. test 7')
+
+
+#___________begining of Peters tests
+
+    def test_set_stuff(self):
+        """
+        Documentation
+        """
+        #making a test mesh
+        p0=[0.,2.]
+        p1=[1.,2.]
+        p2=[0.,1.]
+        p3=[1.,1.]
+        p4=[0.,0.]
+        p5=[2.,0.]
+        p6=[-1.,1.]
+        point_list = [p0,p1,p2,p3,p4,p5,p6]
+
+        a0=[0]
+        a1=[0]
+        a2=[100]
+        a3=[0]
+        a4=[0]
+        a5=[0]
+        a6=[0]
+        attribute=[a0,a1,a2,a3,a4,a5,a6]
+        
+        t0=[0,3,1]
+        t1=[0,2,3]
+        t2=[2,4,3]
+        t3=[4,5,3]
+        t4=[1,3,5]
+        t5=[2,6,4]
+
+        n0=[4,-1,2]
+        n1=[2,0,-1]
+        n2=[3,1,5]
+        n3=[4,2,-1]
+        n4=[3,-1,0]
+        n5=[-1,2,-1]
+
+        tri_list = [t0,t1,t2,t3,t4,t5]
+        n_list = [n0,n1,n2,n3,n4,n5]
+        for i in range(6):
+            for j in (0,1,2):
+                a=attribute[tri_list[i][j]]
+                tri_list[i][j]=point_list[tri_list[i][j]]
+                tri_list[i][j]=Vertex(tri_list[i][j][0]\
+                                      ,tri_list[i][j][1],a)
+            neighbours=n_list[i]
+            tri_list[i]=Triangle(tri_list[i][0],\
+                                 tri_list[i][1],tri_list[i][2]\
+                                ,neighbors=neighbours)
+
+        #testing selectAll
+        mesh = Mesh()
+        mesh.attributeTitles=['attrib']
+
+        mesh.meshTriangles=tri_list
+
+        mesh.selectAllTriangles()
+        A=mesh.sets[mesh.setID['All']]
+        assert list_comp(tri_list,A)
+
+       #testing threshold
+        mesh = Mesh()
+        mesh.attributeTitles=['attrib']
+
+        mesh.meshTriangles=tri_list
+        mesh.selectAllTriangles()
+        mesh.threshold('All',min=30,max=35,attribute_name = 'attrib')
+        A = [tri_list[1],tri_list[2],tri_list[5]]
+        B = mesh.sets[mesh.setID['All']]
+        assert list_comp(A,B)
+       
+
+        A = [tri_list[3],tri_list[2],tri_list[5]]
+        assert not list_comp(A,B)
+
+        #testing 
+
+    def test_Discretised_Tuple_Set_rounding(self):
+        #This is the hardest bit of DST
+        from mesh import Discretised_Tuple_Set
+
+        tol = 0.1
+        a=Discretised_Tuple_Set(p_rel=1,t_rel= tol)
+        m = 0.541
+        m_up = 0.6
+        m_down = 0.5
+        assert m_up == a.round_up_rel(m)
+        assert m_down == a.round_down_rel(m)
+
+        tol = 0.1
+        a=Discretised_Tuple_Set(p_rel=1,t_rel = tol)
+        m = 0.539
+        m_up = 0.5
+        m_down = 0.5
+        assert m_up == a.round_up_rel(m)
+        assert m_down == a.round_down_rel(m)
+
+        tol = 0.5
+        a=Discretised_Tuple_Set(p_rel=1,t_rel = tol)
+
+
+        m = 0.6
+        m_up = 0.7
+        m_down = 0.5
+        assert m_up == a.round_up_rel(m)
+        assert m_down == a.round_down_rel(m)
+
+        m = 0.599
+        m_up = 0.6
+        m_down = 0.5
+        assert m_up == a.round_up_rel(m)
+        assert m_down == a.round_down_rel(m)
+
+    def test_Discretised_Tuple_Set_get(self):
+        from mesh import Discretised_Tuple_Set
+        tol = 0.25
+        a=Discretised_Tuple_Set(p_rel=1,t_rel = tol)
+        b = (1.1,1.1)
+        a.append(b)
+        list = [(1.2,1.),(1.,1.),(1.,1.2),(1.2,1.2)]
+        for key in list:
+            assert a[key][0]==b
+            assert len(a[key])==1
+        
+        c = (2.1,1.)
+        a.append(c)
+        assert a[(2.,1.)][0]==c
+        assert a[(2.2,1.)][0]==c
+
+    def test_mapped_Discretised_Tuple_Set(self):
+
+        from mesh import Mapped_Discretised_Tuple_Set
+        from mesh import Discretised_Tuple_Set
+
+        def map(sequence):
+            return [len(sequence)]
+
+        tol = 0.5
+        a=Mapped_Discretised_Tuple_Set(map,p_rel=1,t_rel = tol)
+        b = range(20)
+        a.append(b)
+        assert b in a[range(17)] 
+        assert b in a[range(22)]
+
+        tol = 0.01
+        a=Mapped_Discretised_Tuple_Set(map,p_rel=1,t_rel = tol)
+        b = range(20)
+        a.append(b)
+        assert b in a[range(20)] 
+        assert b in a[range(19)] 
+        assert not range(17) in a
+
+
+def list_comp(A,B):
+    yes = len(A)==len(B)
+    for item in A:
+        if not item in B:
+            yes = False
+    return yes
+
+#___________end of Peters tests
+            
+#-------------------------------------------------------------
+if __name__ == "__main__":
+    suite = unittest.makeSuite(meshTestCase,'test')
+    #suite = unittest.makeSuite(meshTestCase,'test_asciiFile')
+    runner = unittest.TextTestRunner() #verbosity=2)
+    runner.run(suite)
+    
Index: /inundation/pmesh/timing.py
===================================================================
--- /inundation/pmesh/timing.py	(revision 1719)
+++ /inundation/pmesh/timing.py	(revision 1719)
@@ -0,0 +1,67 @@
+"""
+Script to measure how long pmesh spends doing various methods
+"""
+from mesh import *
+from pmesh import *
+import time
+
+
+draw = Draw()
+#draw.run()
+n = 400
+times = []
+tinitial = time.time()
+times.append(("initial",time.time()-tinitial))
+#------------------------------------------
+mesh = Mesh()
+id = 0
+for i in range(n):
+    for j in range(n):
+       v = mesh.addUserVertex(i,j)
+       v.guiID = id
+       id += 1
+v1 = mesh.addUserVertex(-1,-1)
+v1.guiID = id
+id += 1
+v2 = mesh.addUserVertex(-1,n)
+v2.guiID = id
+id += 1
+v3 = mesh.addUserVertex(n,n)
+v3.guiID = id
+id += 1
+v4 = mesh.addUserVertex(n,-1)
+v4.guiID = id
+id += 1
+mesh.addUserSegment(v1,v2)
+mesh.addUserSegment(v2,v3)
+mesh.addUserSegment(v3,v4)
+mesh.addUserSegment(v4,v1)
+times.append(("user_outline_created",time.time() - tinitial ))
+#------------------------------------------
+#mesh.autoSegment()
+#times.append(("mesh_autosegmented",time.time() - tinitial ))
+#------------------------------------------
+mesh.generateMesh(mode = "Q",maxArea = 1)
+times.append(("mesh_generated",time.time() - tinitial ))
+#------------------------------------------
+draw.mesh = mesh
+draw.selectZoom(1.0)
+times.append(("zoomed",time.time() - tinitial ))
+#------------------------------------------
+
+
+#---------------------
+print "N is ", n
+for time in times:
+    print "%s  %0.12f" %(time[0],  time[1])
+
+
+#Results - mesh.py ver   1.84 	       	1.85	
+# N	400	
+# initial			0		0
+# user_outline_created	1.467999935	1.609999895
+# mesh_generated       	21.70300007	22.3440001
+#zoomed			32.81299996	33.5940001
+
+
+
Index: /inundation/pmesh/toolbarbutton.py
===================================================================
--- /inundation/pmesh/toolbarbutton.py	(revision 1719)
+++ /inundation/pmesh/toolbarbutton.py	(revision 1719)
@@ -0,0 +1,77 @@
+from Tkinter import *
+import string, time
+
+class ToolBarButton(Label):
+    def __init__(self, top, parent, tag=None, image=None, command=None,
+                 statushelp='', balloonhelp='', height=21, width=21,
+                 bd=1, activebackground='lightgrey', padx=0, pady=0,
+                 state='normal', bg='grey'):
+        Label.__init__(self, parent, height=height, width=width,
+                       relief='flat', bd=bd, bg=bg)
+        self.bg = bg 
+        self.activebackground = activebackground
+        if image != None:
+            if string.splitfields(image, '.')[1] == 'bmp':
+                self.Icon = BitmapImage(file='icons/%s' % image)
+            else:
+                self.Icon = PhotoImage(file='icons/%s' % image)
+        else:
+                self.Icon = PhotoImage(file='icons/blank.gif')
+        self.config(image=self.Icon)
+        self.tag = tag
+        self.icommand = command
+        self.command  = self.activate
+        self.bind("<Enter>",           self.buttonEnter)
+        self.bind("<Leave>",           self.buttonLeave)
+        self.bind("<ButtonPress-1>",   self.buttonDown)
+        self.bind("<ButtonRelease-1>", self.buttonUp)
+        self.pack(side='left', anchor=NW, padx=padx, pady=pady)
+        if balloonhelp or statushelp:
+            top.balloon().bind(self, balloonhelp, statushelp)
+        self.state = state    
+      
+    def activate(self):
+        self.icommand(self.tag)
+
+    def buttonEnter(self, event):
+        if self.state != 'disabled':
+            self.config(relief='raised', bg=self.bg)
+
+    def buttonLeave(self, event):
+        if self.state != 'disabled':
+            self.config(relief='flat', bg=self.bg)
+
+    def buttonDown(self, event):
+        if self.state != 'disabled':
+            self.config(relief='sunken', bg=self.activebackground)
+    
+    def buttonUp(self, event):
+        if self.state != 'disabled':
+            if self.command != None:
+                self.command()
+            time.sleep(0.05)
+            if (self in ToolBarButton.cyclelist):
+                if (ToolBarButton.sunkenbutton) and ToolBarButton.sunkenbutton != self:
+                    ToolBarButton.sunkenbutton.config(relief='flat', bg=self.bg)
+                ToolBarButton.sunkenbutton = self
+            else:
+                self.config(relief='flat', bg=self.bg)  
+
+    def enable(self):
+        self.state = 'normal'
+
+    def disable(self):
+        self.state = 'disabled'
+
+    def cycle(self, name):
+        """Add button to list of buttons where one is left sunken, untill
+        the next button is pressed."""
+        ToolBarButton.cyclelist.append(self)
+    def setInitialSunkenButton(self, name):
+        if not ToolBarButton.sunkenbutton:
+            ToolBarButton.sunkenbutton = self
+            self.config(relief='sunken', bg=self.activebackground)
+            
+    #class variable
+    cyclelist = []
+    sunkenbutton = None
Index: /inundation/pmesh/triangle/triangle.c
===================================================================
--- /inundation/pmesh/triangle/triangle.c	(revision 1719)
+++ /inundation/pmesh/triangle/triangle.c	(revision 1719)
@@ -0,0 +1,16008 @@
+/*****************************************************************************/
+/*                                                                           */
+/*      888888888        ,o,                          / 888                  */
+/*         888    88o88o  "    o8888o  88o8888o o88888o 888  o88888o         */
+/*         888    888    888       88b 888  888 888 888 888 d888  88b        */
+/*         888    888    888  o88^o888 888  888 "88888" 888 8888oo888        */
+/*         888    888    888 C888  888 888  888  /      888 q888             */
+/*         888    888    888  "88o^888 888  888 Cb      888  "88oooo"        */
+/*                                              "8oo8D                       */
+/*                                                                           */
+/*  A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.      */
+/*  (triangle.c)                                                             */
+/*                                                                           */
+/*  Version 1.5                                                              */
+/*  April 27, 2004                                                           */
+/*                                                                           */
+/*  Copyright 1993, 1995, 1997, 1998, 2002, 2004                             */
+/*  Jonathan Richard Shewchuk                                                */
+/*  2360 Woolsey #H                                                          */
+/*  Berkeley, California  94705-1927                                         */
+/*  jrs@cs.berkeley.edu                                                      */
+/*                                                                           */
+/*  This program may be freely redistributed under the condition that the    */
+/*    copyright notices (including this entire header and the copyright      */
+/*    notice printed when the `-h' switch is selected) are not removed, and  */
+/*    no compensation is received.  Private, research, and institutional     */
+/*    use is free.  You may distribute modified versions of this code UNDER  */
+/*    THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE   */
+/*    SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE   */
+/*    AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR    */
+/*    NOTICE IS GIVEN OF THE MODIFICATIONS.  Distribution of this code as    */
+/*    part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT  */
+/*    WITH THE AUTHOR.  (If you are not directly supplying this code to a    */
+/*    customer, and you are instead telling them how they can obtain it for  */
+/*    free, then you are not required to make any arrangement with me.)      */
+/*                                                                           */
+/*  Hypertext instructions for Triangle are available on the Web at          */
+/*                                                                           */
+/*      http://www.cs.cmu.edu/~quake/triangle.html                           */
+/*                                                                           */
+/*  Some of the references listed below are marked with an asterisk.  [*]    */
+/*    These references are available for downloading from the Web page       */
+/*                                                                           */
+/*      http://www.cs.cmu.edu/~quake/triangle.research.html                  */
+/*                                                                           */
+/*  Three papers discussing aspects of Triangle are available.  A short      */
+/*    overview appears in "Triangle:  Engineering a 2D Quality Mesh          */
+/*    Generator and Delaunay Triangulator," in Applied Computational         */
+/*    Geometry:  Towards Geometric Engineering, Ming C. Lin and Dinesh       */
+/*    Manocha, editors, Lecture Notes in Computer Science volume 1148,       */
+/*    pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM   */
+/*    Workshop on Applied Computational Geometry).  [*]                      */
+/*                                                                           */
+/*    The algorithms are discussed in the greatest detail in "Delaunay       */
+/*    Refinement Algorithms for Triangular Mesh Generation," Computational   */
+/*    Geometry:  Theory and Applications 22(1-3):21-74, May 2002.  [*]       */
+/*                                                                           */
+/*    More detail about the data structures may be found in my dissertation: */
+/*    "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report  */
+/*    CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */
+/*    Pittsburgh, Pennsylvania, 18 May 1997.  [*]                            */
+/*                                                                           */
+/*  Triangle was created as part of the Archimedes project in the School of  */
+/*    Computer Science at Carnegie Mellon University.  Archimedes is a       */
+/*    system for compiling parallel finite element solvers.  For further     */
+/*    information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F.   */
+/*    Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu,  */
+/*    "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous   */
+/*    Media on Parallel Computers," Computer Methods in Applied Mechanics    */
+/*    and Engineering 152(1-2):85-102, 22 January 1998.                      */
+/*                                                                           */
+/*  Triangle's Delaunay refinement algorithm for quality mesh generation is  */
+/*    a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm   */
+/*    for Quality 2-Dimensional Mesh Generation," Journal of Algorithms      */
+/*    18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */
+/*    Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */
+/*    Annual Symposium on Computational Geometry (San Diego, California),    */
+/*    pages 274-280, Association for Computing Machinery, May 1993.          */
+/*                                                                           */
+/*  The Delaunay refinement algorithm has been modified so that it           */
+/*    consistently meshes domains with small input angles, as described in   */
+/*    my lengthy journal article listed above, or in abbreviated form in     */
+/*    Jonathan Richard Shewchuk, "Mesh Generation for Domains with Small     */
+/*    Angles," Proceedings of the Sixteenth Annual Symposium on              */
+/*    Computational Geometry (Hong Kong), pages 1-10, Association for        */
+/*    Computing Machinery, June 2000.  [*]                                   */
+/*                                                                           */
+/*  My implementation of the divide-and-conquer and incremental Delaunay     */
+/*    triangulation algorithms follows closely the presentation of Guibas    */
+/*    and Stolfi, even though I use a triangle-based data structure instead  */
+/*    of their quad-edge data structure.  (In fact, I originally implemented */
+/*    Triangle using the quad-edge data structure, but the switch to a       */
+/*    triangle-based data structure sped Triangle by a factor of two.)  The  */
+/*    mesh manipulation primitives and the two aforementioned Delaunay       */
+/*    triangulation algorithms are described by Leonidas J. Guibas and Jorge */
+/*    Stolfi, "Primitives for the Manipulation of General Subdivisions and   */
+/*    the Computation of Voronoi Diagrams," ACM Transactions on Graphics     */
+/*    4(2):74-123, April 1985.                                               */
+/*                                                                           */
+/*  Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai   */
+/*    Lee and Bruce J. Schachter, "Two Algorithms for Constructing the       */
+/*    Delaunay Triangulation," International Journal of Computer and         */
+/*    Information Science 9(3):219-242, 1980.  Triangle's improvement of the */
+/*    divide-and-conquer algorithm by alternating between vertical and       */
+/*    horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and-  */
+/*    Conquer Algorithm for Constructing Delaunay Triangulations,"           */
+/*    Algorithmica 2(2):137-151, 1987.                                       */
+/*                                                                           */
+/*  The incremental insertion algorithm was first proposed by C. L. Lawson,  */
+/*    "Software for C1 Surface Interpolation," in Mathematical Software III, */
+/*    John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977.     */
+/*    For point location, I use the algorithm of Ernst P. Mucke, Isaac       */
+/*    Saias, and Binhai Zhu, "Fast Randomized Point Location Without         */
+/*    Preprocessing in Two- and Three-Dimensional Delaunay Triangulations,"  */
+/*    Proceedings of the Twelfth Annual Symposium on Computational Geometry, */
+/*    ACM, May 1996.  [*]  If I were to randomize the order of vertex        */
+/*    insertion (I currently don't bother), their result combined with the   */
+/*    result of Kenneth L. Clarkson and Peter W. Shor, "Applications of      */
+/*    Random Sampling in Computational Geometry II," Discrete &              */
+/*    Computational Geometry 4(1):387-421, 1989, would yield an expected     */
+/*    O(n^{4/3}) bound on running time.                                      */
+/*                                                                           */
+/*  The O(n log n) sweepline Delaunay triangulation algorithm is taken from  */
+/*    Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams",          */
+/*    Algorithmica 2(2):153-174, 1987.  A random sample of edges on the      */
+/*    boundary of the triangulation are maintained in a splay tree for the   */
+/*    purpose of point location.  Splay trees are described by Daniel        */
+/*    Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */
+/*    Trees," Journal of the ACM 32(3):652-686, July 1985.                   */
+/*                                                                           */
+/*  The algorithms for exact computation of the signs of determinants are    */
+/*    described in Jonathan Richard Shewchuk, "Adaptive Precision Floating-  */
+/*    Point Arithmetic and Fast Robust Geometric Predicates," Discrete &     */
+/*    Computational Geometry 18(3):305-363, October 1997.  (Also available   */
+/*    as Technical Report CMU-CS-96-140, School of Computer Science,         */
+/*    Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.)  [*]  */
+/*    An abbreviated version appears as Jonathan Richard Shewchuk, "Robust   */
+/*    Adaptive Floating-Point Geometric Predicates," Proceedings of the      */
+/*    Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */
+/*    Many of the ideas for my exact arithmetic routines originate with      */
+/*    Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point  */
+/*    Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */
+/*    Computer Society Press, 1991.  [*]  Many of the ideas for the correct  */
+/*    evaluation of the signs of determinants are taken from Steven Fortune  */
+/*    and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa-   */
+/*    tional Geometry," Proceedings of the Ninth Annual Symposium on         */
+/*    Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven    */
+/*    Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu-   */
+/*    lations," International Journal of Computational Geometry & Applica-   */
+/*    tions 5(1-2):193-213, March-June 1995.                                 */
+/*                                                                           */
+/*  The method of inserting new vertices off-center (not precisely at the    */
+/*    circumcenter of every poor-quality triangle) is from Alper Ungor,      */
+/*    "Off-centers:  A New Type of Steiner Points for Computing Size-Optimal */
+/*    quality-guaranteed Delaunay triangulations," Proceedings of LATIN      */
+/*    2004 (Buenos Aires, Argentina), April 2004.                            */
+/*                                                                           */
+/*  For definitions of and results involving Delaunay triangulations,        */
+/*    constrained and conforming versions thereof, and other aspects of      */
+/*    triangular mesh generation, see the excellent survey by Marshall Bern  */
+/*    and David Eppstein, "Mesh Generation and Optimal Triangulation," in    */
+/*    Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang,         */
+/*    editors, World Scientific, Singapore, pp. 23-90, 1992.  [*]            */
+/*                                                                           */
+/*  The time for incrementally adding PSLG (planar straight line graph)      */
+/*    segments to create a constrained Delaunay triangulation is probably    */
+/*    O(t^2) per segment in the worst case and O(t) per segment in the       */
+/*    common case, where t is the number of triangles that intersect the     */
+/*    segment before it is inserted.  This doesn't count point location,     */
+/*    which can be much more expensive.  I could improve this to O(d log d)  */
+/*    time, but d is usually quite small, so it's not worth the bother.      */
+/*    (This note does not apply to conforming Delaunay triangulations, for   */
+/*    which a different method is used to insert segments.)                  */
+/*                                                                           */
+/*  The time for adding segments to a conforming Delaunay triangulation is   */
+/*    not clear, but does not depend upon t alone.  In some cases, very      */
+/*    small features (like a vertex lying next to a segment) can cause a     */
+/*    single segment to be split an arbitrary number of times.  Of course,   */
+/*    floating-point precision is a practical barrier to how much this can   */
+/*    happen.                                                                */
+/*                                                                           */
+/*  The time for deleting a vertex from a Delaunay triangulation is O(d^2)   */
+/*    in the worst case and O(d) in the common case, where d is the degree   */
+/*    of the vertex being deleted.  I could improve this to O(d log d) time, */
+/*    but d is usually quite small, so it's not worth the bother.            */
+/*                                                                           */
+/*  Ruppert's Delaunay refinement algorithm typically generates triangles    */
+/*    at a linear rate (constant time per triangle) after the initial        */
+/*    triangulation is formed.  There may be pathological cases where        */
+/*    quadratic time is required, but these never arise in practice.         */
+/*                                                                           */
+/*  The geometric predicates (circumcenter calculations, segment             */
+/*    intersection formulae, etc.) appear in my "Lecture Notes on Geometric  */
+/*    Robustness" at http://www.cs.berkeley.edu/~jrs/mesh.html .             */
+/*                                                                           */
+/*  If you make any improvements to this code, please please please let me   */
+/*    know, so that I may obtain the improvements.  Even if you don't change */
+/*    the code, I'd still love to hear what it's being used for.             */
+/*                                                                           */
+/*  Disclaimer:  Neither I nor Carnegie Mellon warrant this code in any way  */
+/*    whatsoever.  This code is provided "as-is".  Use at your own risk.     */
+/*                                                                           */
+/*****************************************************************************/
+
+/* For single precision (which will save some memory and reduce paging),     */
+/*   define the symbol SINGLE by using the -DSINGLE compiler switch or by    */
+/*   writing "#define SINGLE" below.                                         */
+/*                                                                           */
+/* For double precision (which will allow you to refine meshes to a smaller  */
+/*   edge length), leave SINGLE undefined.                                   */
+/*                                                                           */
+/* Double precision uses more memory, but improves the resolution of the     */
+/*   meshes you can generate with Triangle.  It also reduces the likelihood  */
+/*   of a floating exception due to overflow.  Finally, it is much faster    */
+/*   than single precision on 64-bit architectures like the DEC Alpha.  I    */
+/*   recommend double precision unless you want to generate a mesh for which */
+/*   you do not have enough memory.                                          */
+
+/* #define SINGLE */
+
+#ifdef SINGLE
+#define REAL float
+#else /* not SINGLE */
+#define REAL double
+#endif /* not SINGLE */
+
+/* If yours is not a Unix system, define the NO_TIMER compiler switch to     */
+/*   remove the Unix-specific timing code.                                   */
+
+/* #define NO_TIMER */
+
+/* To insert lots of self-checks for internal errors, define the SELF_CHECK  */
+/*   symbol.  This will slow down the program significantly.  It is best to  */
+/*   define the symbol using the -DSELF_CHECK compiler switch, but you could */
+/*   write "#define SELF_CHECK" below.  If you are modifying this code, I    */
+/*   recommend you turn self-checks on until your work is debugged.          */
+
+/* #define SELF_CHECK */
+
+/* To compile Triangle as a callable object library (triangle.o), define the */
+/*   TRILIBRARY symbol.  Read the file triangle.h for details on how to call */
+/*   the procedure triangulate() that results.                               */
+
+/* #define TRILIBRARY */
+
+/* It is possible to generate a smaller version of Triangle using one or     */
+/*   both of the following symbols.  Define the REDUCED symbol to eliminate  */
+/*   all features that are primarily of research interest; specifically, the */
+/*   -i, -F, -s, and -C switches.  Define the CDT_ONLY symbol to eliminate   */
+/*   all meshing algorithms above and beyond constrained Delaunay            */
+/*   triangulation; specifically, the -r, -q, -a, -S, and -s switches.       */
+/*   These reductions are most likely to be useful when generating an object */
+/*   library (triangle.o) by defining the TRILIBRARY symbol.                 */
+
+/* #define REDUCED */
+/* #define CDT_ONLY */
+
+/* On some machines, my exact arithmetic routines might be defeated by the   */
+/*   use of internal extended precision floating-point registers.  The best  */
+/*   way to solve this problem is to set the floating-point registers to use */
+/*   single or double precision internally.  On 80x86 processors, this may   */
+/*   be accomplished by setting the CPU86 symbol for the Microsoft C         */
+/*   compiler, or the LINUX symbol for the gcc compiler running on Linux.    */
+/*                                                                           */
+/* An inferior solution is to declare certain values as `volatile', thus     */
+/*   forcing them to be stored to memory and rounded off.  Unfortunately,    */
+/*   this solution might slow Triangle down quite a bit.  To use volatile    */
+/*   values, write "#define INEXACT volatile" below.  Normally, however,     */
+/*   INEXACT should be defined to be nothing.  ("#define INEXACT".)          */
+/*                                                                           */
+/* For more discussion, see http://www.cs.cmu.edu/~quake/robust.pc.html .    */
+/*   For yet more discussion, see Section 5 of my paper, "Adaptive Precision */
+/*   Floating-Point Arithmetic and Fast Robust Geometric Predicates" (also   */
+/*   available as Section 6.6 of my dissertation).                           */
+
+/* #define CPU86 */
+/* #define LINUX */
+
+#define INEXACT /* Nothing */
+/* #define INEXACT volatile */
+
+/* Maximum number of characters in a file name (including the null).         */
+
+#define FILENAMESIZE 512
+
+/* Maximum number of characters in a line read from a file (including the    */
+/*   null).                                                                  */
+
+#define INPUTLINESIZE 512
+
+/* For efficiency, a variety of data structures are allocated in bulk.  The  */
+/*   following constants determine how many of each structure is allocated   */
+/*   at once.                                                                */
+
+#define TRIPERBLOCK 4092           /* Number of triangles allocated at once. */
+#define SUBSEGPERBLOCK 508       /* Number of subsegments allocated at once. */
+#define VERTEXPERBLOCK 4092         /* Number of vertices allocated at once. */
+#define VIRUSPERBLOCK 1020   /* Number of virus triangles allocated at once. */
+/* Number of encroached subsegments allocated at once. */
+#define BADSUBSEGPERBLOCK 252
+/* Number of skinny triangles allocated at once. */
+#define BADTRIPERBLOCK 4092
+/* Number of flipped triangles allocated at once. */
+#define FLIPSTACKERPERBLOCK 252
+/* Number of splay tree nodes allocated at once. */
+#define SPLAYNODEPERBLOCK 508
+
+/* The vertex types.   A DEADVERTEX has been deleted entirely.  An           */
+/*   UNDEADVERTEX is not part of the mesh, but is written to the output      */
+/*   .node file and affects the node indexing in the other output files.     */
+
+#define INPUTVERTEX 0
+#define SEGMENTVERTEX 1
+#define FREEVERTEX 2
+#define DEADVERTEX -32768
+#define UNDEADVERTEX -32767
+
+/* The next line is used to outsmart some very stupid compilers.  If your    */
+/*   compiler is smarter, feel free to replace the "int" with "void".        */
+/*   Not that it matters.                                                    */
+
+#define VOID int
+
+/* Two constants for algorithms based on random sampling.  Both constants    */
+/*   have been chosen empirically to optimize their respective algorithms.   */
+
+/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide    */
+/*   how large a random sample of triangles to inspect.                      */
+
+#define SAMPLEFACTOR 11
+
+/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */
+/*   of boundary edges should be maintained in the splay tree for point      */
+/*   location on the front.                                                  */
+
+#define SAMPLERATE 10
+
+/* A number that speaks for itself, every kissable digit.                    */
+
+#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
+
+/* Another fave.                                                             */
+
+#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732
+
+/* And here's one for those of you who are intimidated by math.              */
+
+#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#ifndef NO_TIMER
+#include <sys/time.h>
+#endif /* not NO_TIMER */
+#ifdef CPU86
+#include <float.h>
+#endif /* CPU86 */
+#ifdef LINUX
+#include <fpu_control.h>
+#endif /* LINUX */
+#ifdef TRILIBRARY
+#include "triangle.h"
+#endif /* TRILIBRARY */
+
+/* A few forward declarations.                                               */
+
+#ifndef TRILIBRARY
+char *readline();
+char *findfield();
+#endif /* not TRILIBRARY */
+
+/* Labels that signify whether a record consists primarily of pointers or of */
+/*   floating-point words.  Used to make decisions about data alignment.     */
+
+enum wordtype {POINTER, FLOATINGPOINT};
+
+/* Labels that signify the result of point location.  The result of a        */
+/*   search indicates that the point falls in the interior of a triangle, on */
+/*   an edge, on a vertex, or outside the mesh.                              */
+
+enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE};
+
+/* Labels that signify the result of vertex insertion.  The result indicates */
+/*   that the vertex was inserted with complete success, was inserted but    */
+/*   encroaches upon a subsegment, was not inserted because it lies on a     */
+/*   segment, or was not inserted because another vertex occupies the same   */
+/*   location.                                                               */
+
+enum insertvertexresult {SUCCESSFULVERTEX, ENCROACHINGVERTEX, VIOLATINGVERTEX,
+                         DUPLICATEVERTEX};
+
+/* Labels that signify the result of direction finding.  The result          */
+/*   indicates that a segment connecting the two query points falls within   */
+/*   the direction triangle, along the left edge of the direction triangle,  */
+/*   or along the right edge of the direction triangle.                      */
+
+enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR};
+
+/*****************************************************************************/
+/*                                                                           */
+/*  The basic mesh data structures                                           */
+/*                                                                           */
+/*  There are three:  vertices, triangles, and subsegments (abbreviated      */
+/*  `subseg').  These three data structures, linked by pointers, comprise    */
+/*  the mesh.  A vertex simply represents a mesh vertex and its properties.  */
+/*  A triangle is a triangle.  A subsegment is a special data structure used */
+/*  to represent an impenetrable edge of the mesh (perhaps on the outer      */
+/*  boundary, on the boundary of a hole, or part of an internal boundary     */
+/*  separating two triangulated regions).  Subsegments represent boundaries, */
+/*  defined by the user, that triangles may not lie across.                  */
+/*                                                                           */
+/*  A triangle consists of a list of three vertices, a list of three         */
+/*  adjoining triangles, a list of three adjoining subsegments (when         */
+/*  segments exist), an arbitrary number of optional user-defined            */
+/*  floating-point attributes, and an optional area constraint.  The latter  */
+/*  is an upper bound on the permissible area of each triangle in a region,  */
+/*  used for mesh refinement.                                                */
+/*                                                                           */
+/*  For a triangle on a boundary of the mesh, some or all of the neighboring */
+/*  triangles may not be present.  For a triangle in the interior of the     */
+/*  mesh, often no neighboring subsegments are present.  Such absent         */
+/*  triangles and subsegments are never represented by NULL pointers; they   */
+/*  are represented by two special records:  `dummytri', the triangle that   */
+/*  fills "outer space", and `dummysub', the omnipresent subsegment.         */
+/*  `dummytri' and `dummysub' are used for several reasons; for instance,    */
+/*  they can be dereferenced and their contents examined without violating   */
+/*  protected memory.                                                        */
+/*                                                                           */
+/*  However, it is important to understand that a triangle includes other    */
+/*  information as well.  The pointers to adjoining vertices, triangles, and */
+/*  subsegments are ordered in a way that indicates their geometric relation */
+/*  to each other.  Furthermore, each of these pointers contains orientation */
+/*  information.  Each pointer to an adjoining triangle indicates which face */
+/*  of that triangle is contacted.  Similarly, each pointer to an adjoining  */
+/*  subsegment indicates which side of that subsegment is contacted, and how */
+/*  the subsegment is oriented relative to the triangle.                     */
+/*                                                                           */
+/*  The data structure representing a subsegment may be thought to be        */
+/*  abutting the edge of one or two triangle data structures:  either        */
+/*  sandwiched between two triangles, or resting against one triangle on an  */
+/*  exterior boundary or hole boundary.                                      */
+/*                                                                           */
+/*  A subsegment consists of a list of two vertices, a list of two           */
+/*  adjoining subsegments, and a list of two adjoining triangles.  One of    */
+/*  the two adjoining triangles may not be present (though there should      */
+/*  always be one), and neighboring subsegments might not be present.        */
+/*  Subsegments also store a user-defined integer "boundary marker".         */
+/*  Typically, this integer is used to indicate what boundary conditions are */
+/*  to be applied at that location in a finite element simulation.           */
+/*                                                                           */
+/*  Like triangles, subsegments maintain information about the relative      */
+/*  orientation of neighboring objects.                                      */
+/*                                                                           */
+/*  Vertices are relatively simple.  A vertex is a list of floating-point    */
+/*  numbers, starting with the x, and y coordinates, followed by an          */
+/*  arbitrary number of optional user-defined floating-point attributes,     */
+/*  followed by an integer boundary marker.  During the segment insertion    */
+/*  phase, there is also a pointer from each vertex to a triangle that may   */
+/*  contain it.  Each pointer is not always correct, but when one is, it     */
+/*  speeds up segment insertion.  These pointers are assigned values once    */
+/*  at the beginning of the segment insertion phase, and are not used or     */
+/*  updated except during this phase.  Edge flipping during segment          */
+/*  insertion will render some of them incorrect.  Hence, don't rely upon    */
+/*  them for anything.                                                       */
+/*                                                                           */
+/*  Other than the exception mentioned above, vertices have no information   */
+/*  about what triangles, subfacets, or subsegments they are linked to.      */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Handles                                                                  */
+/*                                                                           */
+/*  The oriented triangle (`otri') and oriented subsegment (`osub') data     */
+/*  structures defined below do not themselves store any part of the mesh.   */
+/*  The mesh itself is made of `triangle's, `subseg's, and `vertex's.        */
+/*                                                                           */
+/*  Oriented triangles and oriented subsegments will usually be referred to  */
+/*  as "handles."  A handle is essentially a pointer into the mesh; it       */
+/*  allows you to "hold" one particular part of the mesh.  Handles are used  */
+/*  to specify the regions in which one is traversing and modifying the mesh.*/
+/*  A single `triangle' may be held by many handles, or none at all.  (The   */
+/*  latter case is not a memory leak, because the triangle is still          */
+/*  connected to other triangles in the mesh.)                               */
+/*                                                                           */
+/*  An `otri' is a handle that holds a triangle.  It holds a specific edge   */
+/*  of the triangle.  An `osub' is a handle that holds a subsegment.  It     */
+/*  holds either the left or right side of the subsegment.                   */
+/*                                                                           */
+/*  Navigation about the mesh is accomplished through a set of mesh          */
+/*  manipulation primitives, further below.  Many of these primitives take   */
+/*  a handle and produce a new handle that holds the mesh near the first     */
+/*  handle.  Other primitives take two handles and glue the corresponding    */
+/*  parts of the mesh together.  The orientation of the handles is           */
+/*  important.  For instance, when two triangles are glued together by the   */
+/*  bond() primitive, they are glued at the edges on which the handles lie.  */
+/*                                                                           */
+/*  Because vertices have no information about which triangles they are      */
+/*  attached to, I commonly represent a vertex by use of a handle whose      */
+/*  origin is the vertex.  A single handle can simultaneously represent a    */
+/*  triangle, an edge, and a vertex.                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+/* The triangle data structure.  Each triangle contains three pointers to    */
+/*   adjoining triangles, plus three pointers to vertices, plus three        */
+/*   pointers to subsegments (declared below; these pointers are usually     */
+/*   `dummysub').  It may or may not also contain user-defined attributes    */
+/*   and/or a floating-point "area constraint."  It may also contain extra   */
+/*   pointers for nodes, when the user asks for high-order elements.         */
+/*   Because the size and structure of a `triangle' is not decided until     */
+/*   runtime, I haven't simply declared the type `triangle' as a struct.     */
+
+typedef REAL **triangle;            /* Really:  typedef triangle *triangle   */
+
+/* An oriented triangle:  includes a pointer to a triangle and orientation.  */
+/*   The orientation denotes an edge of the triangle.  Hence, there are      */
+/*   three possible orientations.  By convention, each edge always points    */
+/*   counterclockwise about the corresponding triangle.                      */
+
+struct otri {
+  triangle *tri;
+  int orient;                                         /* Ranges from 0 to 2. */
+};
+
+/* The subsegment data structure.  Each subsegment contains two pointers to  */
+/*   adjoining subsegments, plus two pointers to vertices, plus two pointers */
+/*   to adjoining triangles, plus one boundary marker.                       */
+
+typedef REAL **subseg;                  /* Really:  typedef subseg *subseg   */
+
+/* An oriented subsegment:  includes a pointer to a subsegment and an        */
+/*   orientation.  The orientation denotes a side of the edge.  Hence, there */
+/*   are two possible orientations.  By convention, the edge is always       */
+/*   directed so that the "side" denoted is the right side of the edge.      */
+
+struct osub {
+  subseg *ss;
+  int ssorient;                                       /* Ranges from 0 to 1. */
+};
+
+/* The vertex data structure.  Each vertex is actually an array of REALs.    */
+/*   The number of REALs is unknown until runtime.  An integer boundary      */
+/*   marker, and sometimes a pointer to a triangle, is appended after the    */
+/*   REALs.                                                                  */
+
+typedef REAL *vertex;
+
+/* A queue used to store encroached subsegments.  Each subsegment's vertices */
+/*   are stored so that we can check whether a subsegment is still the same. */
+
+struct badsubseg {
+  subseg encsubseg;                             /* An encroached subsegment. */
+  vertex subsegorg, subsegdest;                         /* Its two vertices. */
+};
+
+/* A queue used to store bad triangles.  The key is the square of the cosine */
+/*   of the smallest angle of the triangle.  Each triangle's vertices are    */
+/*   stored so that one can check whether a triangle is still the same.      */
+
+struct badtriang {
+  triangle poortri;                       /* A skinny or too-large triangle. */
+  REAL key;                             /* cos^2 of smallest (apical) angle. */
+  vertex triangorg, triangdest, triangapex;           /* Its three vertices. */
+  struct badtriang *nexttriang;             /* Pointer to next bad triangle. */
+};
+
+/* A stack of triangles flipped during the most recent vertex insertion.     */
+/*   The stack is used to undo the vertex insertion if the vertex encroaches */
+/*   upon a subsegment.                                                      */
+
+struct flipstacker {
+  triangle flippedtri;                       /* A recently flipped triangle. */
+  struct flipstacker *prevflip;               /* Previous flip in the stack. */
+};
+
+/* A node in a heap used to store events for the sweepline Delaunay          */
+/*   algorithm.  Nodes do not point directly to their parents or children in */
+/*   the heap.  Instead, each node knows its position in the heap, and can   */
+/*   look up its parent and children in a separate array.  The `eventptr'    */
+/*   points either to a `vertex' or to a triangle (in encoded format, so     */
+/*   that an orientation is included).  In the latter case, the origin of    */
+/*   the oriented triangle is the apex of a "circle event" of the sweepline  */
+/*   algorithm.  To distinguish site events from circle events, all circle   */
+/*   events are given an invalid (smaller than `xmin') x-coordinate `xkey'.  */
+
+struct event {
+  REAL xkey, ykey;                              /* Coordinates of the event. */
+  VOID *eventptr;      /* Can be a vertex or the location of a circle event. */
+  int heapposition;              /* Marks this event's position in the heap. */
+};
+
+/* A node in the splay tree.  Each node holds an oriented ghost triangle     */
+/*   that represents a boundary edge of the growing triangulation.  When a   */
+/*   circle event covers two boundary edges with a triangle, so that they    */
+/*   are no longer boundary edges, those edges are not immediately deleted   */
+/*   from the tree; rather, they are lazily deleted when they are next       */
+/*   encountered.  (Since only a random sample of boundary edges are kept    */
+/*   in the tree, lazy deletion is faster.)  `keydest' is used to verify     */
+/*   that a triangle is still the same as when it entered the splay tree; if */
+/*   it has been rotated (due to a circle event), it no longer represents a  */
+/*   boundary edge and should be deleted.                                    */
+
+struct splaynode {
+  struct otri keyedge;                     /* Lprev of an edge on the front. */
+  vertex keydest;           /* Used to verify that splay node is still live. */
+  struct splaynode *lchild, *rchild;              /* Children in splay tree. */
+};
+
+/* A type used to allocate memory.  firstblock is the first block of items.  */
+/*   nowblock is the block from which items are currently being allocated.   */
+/*   nextitem points to the next slab of free memory for an item.            */
+/*   deaditemstack is the head of a linked list (stack) of deallocated items */
+/*   that can be recycled.  unallocateditems is the number of items that     */
+/*   remain to be allocated from nowblock.                                   */
+/*                                                                           */
+/* Traversal is the process of walking through the entire list of items, and */
+/*   is separate from allocation.  Note that a traversal will visit items on */
+/*   the "deaditemstack" stack as well as live items.  pathblock points to   */
+/*   the block currently being traversed.  pathitem points to the next item  */
+/*   to be traversed.  pathitemsleft is the number of items that remain to   */
+/*   be traversed in pathblock.                                              */
+/*                                                                           */
+/* itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest   */
+/*   what sort of word the record is primarily made up of.  alignbytes       */
+/*   determines how new records should be aligned in memory.  itembytes and  */
+/*   itemwords are the length of a record in bytes (after rounding up) and   */
+/*   words.  itemsperblock is the number of items allocated at once in a     */
+/*   single block.  itemsfirstblock is the number of items in the first      */
+/*   block, which can vary from the others.  items is the number of          */
+/*   currently allocated items.  maxitems is the maximum number of items     */
+/*   that have been allocated at once; it is the current number of items     */
+/*   plus the number of records kept on deaditemstack.                       */
+
+struct memorypool {
+  VOID **firstblock, **nowblock;
+  VOID *nextitem;
+  VOID *deaditemstack;
+  VOID **pathblock;
+  VOID *pathitem;
+  enum wordtype itemwordtype;
+  int alignbytes;
+  int itembytes, itemwords;
+  int itemsperblock;
+  int itemsfirstblock;
+  long items, maxitems;
+  int unallocateditems;
+  int pathitemsleft;
+};
+
+
+/* Global constants.                                                         */
+
+REAL splitter;       /* Used to split REAL factors for exact multiplication. */
+REAL epsilon;                             /* Floating-point machine epsilon. */
+REAL resulterrbound;
+REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
+REAL iccerrboundA, iccerrboundB, iccerrboundC;
+REAL o3derrboundA, o3derrboundB, o3derrboundC;
+
+/* Random number seed is not constant, but I've made it global anyway.       */
+
+unsigned long randomseed;                     /* Current random number seed. */
+
+
+/* Mesh data structure.  Triangle operates on only one mesh, but the mesh    */
+/*   structure is used (instead of global variables) to allow reentrancy.    */
+
+struct mesh {
+
+/* Variables used to allocate memory for triangles, subsegments, vertices,   */
+/*   viri (triangles being eaten), encroached segments, bad (skinny or too   */
+/*   large) triangles, and splay tree nodes.                                 */
+
+  struct memorypool triangles;
+  struct memorypool subsegs;
+  struct memorypool vertices;
+  struct memorypool viri;
+  struct memorypool badsubsegs;
+  struct memorypool badtriangles;
+  struct memorypool flipstackers;
+  struct memorypool splaynodes;
+
+/* Variables that maintain the bad triangle queues.  The queues are          */
+/*   ordered from 63 (highest priority) to 0 (lowest priority).              */
+
+  struct badtriang *queuefront[64];
+  struct badtriang *queuetail[64];
+  int nextnonemptyq[64];
+  int firstnonemptyq;
+
+/* Variable that maintains the stack of recently flipped triangles.          */
+
+  struct flipstacker *lastflip;
+
+/* Other variables. */
+
+  REAL xmin, xmax, ymin, ymax;                            /* x and y bounds. */
+  REAL xminextreme;      /* Nonexistent x value used as a flag in sweepline. */
+  int invertices;                               /* Number of input vertices. */
+  int inelements;                              /* Number of input triangles. */
+  int insegments;                               /* Number of input segments. */
+  int holes;                                       /* Number of input holes. */
+  int regions;                                   /* Number of input regions. */
+  int undeads;    /* Number of input vertices that don't appear in the mesh. */
+  long edges;                                     /* Number of output edges. */
+  int mesh_dim;                                /* Dimension (ought to be 2). */
+  int nextras;                           /* Number of attributes per vertex. */
+  int eextras;                         /* Number of attributes per triangle. */
+  long hullsize;                          /* Number of edges in convex hull. */
+  int steinerleft;                 /* Number of Steiner points not yet used. */
+  int vertexmarkindex;         /* Index to find boundary marker of a vertex. */
+  int vertex2triindex;     /* Index to find a triangle adjacent to a vertex. */
+  int highorderindex;  /* Index to find extra nodes for high-order elements. */
+  int elemattribindex;            /* Index to find attributes of a triangle. */
+  int areaboundindex;             /* Index to find area bound of a triangle. */
+  int checksegments;         /* Are there segments in the triangulation yet? */
+  int checkquality;                  /* Has quality triangulation begun yet? */
+  int readnodefile;                           /* Has a .node file been read? */
+  long samples;              /* Number of random samples for point location. */
+
+  long incirclecount;                 /* Number of incircle tests performed. */
+  long counterclockcount;     /* Number of counterclockwise tests performed. */
+  long orient3dcount;           /* Number of 3D orientation tests performed. */
+  long hyperbolacount;      /* Number of right-of-hyperbola tests performed. */
+  long circumcentercount;  /* Number of circumcenter calculations performed. */
+  long circletopcount;       /* Number of circle top calculations performed. */
+
+/* Triangular bounding box vertices.                                         */
+
+  vertex infvertex1, infvertex2, infvertex3;
+
+/* Pointer to the `triangle' that occupies all of "outer space."             */
+
+  triangle *dummytri;
+  triangle *dummytribase;    /* Keep base address so we can free() it later. */
+
+/* Pointer to the omnipresent subsegment.  Referenced by any triangle or     */
+/*   subsegment that isn't really connected to a subsegment at that          */
+/*   location.                                                               */
+
+  subseg *dummysub;
+  subseg *dummysubbase;      /* Keep base address so we can free() it later. */
+
+/* Pointer to a recently visited triangle.  Improves point location if       */
+/*   proximate vertices are inserted sequentially.                           */
+
+  struct otri recenttri;
+
+};                                                  /* End of `struct mesh'. */
+
+
+/* Data structure for command line switches and file names.  This structure  */
+/*   is used (instead of global variables) to allow reentrancy.              */
+
+struct behavior {
+
+/* Switches for the triangulator.                                            */
+/*   poly: -p switch.  refine: -r switch.                                    */
+/*   quality: -q switch.                                                     */
+/*     minangle: minimum angle bound, specified after -q switch.             */
+/*     goodangle: cosine squared of minangle.                                */
+/*     offconstant: constant used to place off-center Steiner points.        */
+/*   vararea: -a switch without number.                                      */
+/*   fixedarea: -a switch with number.                                       */
+/*     maxarea: maximum area bound, specified after -a switch.               */
+/*   usertest: -u switch.                                                    */
+/*   regionattrib: -A switch.  convex: -c switch.                            */
+/*   weighted: 1 for -w switch, 2 for -W switch.  jettison: -j switch        */
+/*   firstnumber: inverse of -z switch.  All items are numbered starting     */
+/*     from `firstnumber'.                                                   */
+/*   edgesout: -e switch.  voronoi: -v switch.                               */
+/*   neighbors: -n switch.  geomview: -g switch.                             */
+/*   nobound: -B switch.  nopolywritten: -P switch.                          */
+/*   nonodewritten: -N switch.  noelewritten: -E switch.                     */
+/*   noiterationnum: -I switch.  noholes: -O switch.                         */
+/*   noexact: -X switch.                                                     */
+/*   order: element order, specified after -o switch.                        */
+/*   nobisect: count of how often -Y switch is selected.                     */
+/*   steiner: maximum number of Steiner points, specified after -S switch.   */
+/*   incremental: -i switch.  sweepline: -F switch.                          */
+/*   dwyer: inverse of -l switch.                                            */
+/*   splitseg: -s switch.                                                    */
+/*   nolenses: -L switch.  docheck: -C switch.                               */
+/*   quiet: -Q switch.  verbose: count of how often -V switch is selected.   */
+/*   usesegments: -p, -r, -q, or -c switch; determines whether segments are  */
+/*     used at all.                                                          */
+/*                                                                           */
+/* Read the instructions to find out the meaning of these switches.          */
+
+  int poly, refine, quality, vararea, fixedarea, usertest;
+  int regionattrib, convex, weighted, jettison;
+  int firstnumber;
+  int edgesout, voronoi, neighbors, geomview;
+  int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum;
+  int noholes, noexact, nolenses;
+  int incremental, sweepline, dwyer;
+  int splitseg;
+  int docheck;
+  int quiet, verbose;
+  int usesegments;
+  int order;
+  int nobisect;
+  int steiner;
+  REAL minangle, goodangle, offconstant;
+  REAL maxarea;
+
+/* Variables for file names.                                                 */
+
+#ifndef TRILIBRARY
+  char innodefilename[FILENAMESIZE];
+  char inelefilename[FILENAMESIZE];
+  char inpolyfilename[FILENAMESIZE];
+  char areafilename[FILENAMESIZE];
+  char outnodefilename[FILENAMESIZE];
+  char outelefilename[FILENAMESIZE];
+  char outpolyfilename[FILENAMESIZE];
+  char edgefilename[FILENAMESIZE];
+  char vnodefilename[FILENAMESIZE];
+  char vedgefilename[FILENAMESIZE];
+  char neighborfilename[FILENAMESIZE];
+  char offfilename[FILENAMESIZE];
+#endif /* not TRILIBRARY */
+
+};                                              /* End of `struct behavior'. */
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Mesh manipulation primitives.  Each triangle contains three pointers to  */
+/*  other triangles, with orientations.  Each pointer points not to the      */
+/*  first byte of a triangle, but to one of the first three bytes of a       */
+/*  triangle.  It is necessary to extract both the triangle itself and the   */
+/*  orientation.  To save memory, I keep both pieces of information in one   */
+/*  pointer.  To make this possible, I assume that all triangles are aligned */
+/*  to four-byte boundaries.  The decode() routine below decodes a pointer,  */
+/*  extracting an orientation (in the range 0 to 2) and a pointer to the     */
+/*  beginning of a triangle.  The encode() routine compresses a pointer to a */
+/*  triangle and an orientation into a single pointer.  My assumptions that  */
+/*  triangles are four-byte-aligned and that the `unsigned long' type is     */
+/*  long enough to hold a pointer are two of the few kludges in this program.*/
+/*                                                                           */
+/*  Subsegments are manipulated similarly.  A pointer to a subsegment        */
+/*  carries both an address and an orientation in the range 0 to 1.          */
+/*                                                                           */
+/*  The other primitives take an oriented triangle or oriented subsegment,   */
+/*  and return an oriented triangle or oriented subsegment or vertex; or     */
+/*  they change the connections in the data structure.                       */
+/*                                                                           */
+/*  Below, triangles and subsegments are denoted by their vertices.  The     */
+/*  triangle abc has origin (org) a, destination (dest) b, and apex (apex)   */
+/*  c.  These vertices occur in counterclockwise order about the triangle.   */
+/*  The handle abc may simultaneously denote vertex a, edge ab, and triangle */
+/*  abc.                                                                     */
+/*                                                                           */
+/*  Similarly, the subsegment ab has origin (sorg) a and destination (sdest) */
+/*  b.  If ab is thought to be directed upward (with b directly above a),    */
+/*  then the handle ab is thought to grasp the right side of ab, and may     */
+/*  simultaneously denote vertex a and edge ab.                              */
+/*                                                                           */
+/*  An asterisk (*) denotes a vertex whose identity is unknown.              */
+/*                                                                           */
+/*  Given this notation, a partial list of mesh manipulation primitives      */
+/*  follows.                                                                 */
+/*                                                                           */
+/*                                                                           */
+/*  For triangles:                                                           */
+/*                                                                           */
+/*  sym:  Find the abutting triangle; same edge.                             */
+/*  sym(abc) -> ba*                                                          */
+/*                                                                           */
+/*  lnext:  Find the next edge (counterclockwise) of a triangle.             */
+/*  lnext(abc) -> bca                                                        */
+/*                                                                           */
+/*  lprev:  Find the previous edge (clockwise) of a triangle.                */
+/*  lprev(abc) -> cab                                                        */
+/*                                                                           */
+/*  onext:  Find the next edge counterclockwise with the same origin.        */
+/*  onext(abc) -> ac*                                                        */
+/*                                                                           */
+/*  oprev:  Find the next edge clockwise with the same origin.               */
+/*  oprev(abc) -> a*b                                                        */
+/*                                                                           */
+/*  dnext:  Find the next edge counterclockwise with the same destination.   */
+/*  dnext(abc) -> *ba                                                        */
+/*                                                                           */
+/*  dprev:  Find the next edge clockwise with the same destination.          */
+/*  dprev(abc) -> cb*                                                        */
+/*                                                                           */
+/*  rnext:  Find the next edge (counterclockwise) of the adjacent triangle.  */
+/*  rnext(abc) -> *a*                                                        */
+/*                                                                           */
+/*  rprev:  Find the previous edge (clockwise) of the adjacent triangle.     */
+/*  rprev(abc) -> b**                                                        */
+/*                                                                           */
+/*  org:  Origin          dest:  Destination          apex:  Apex            */
+/*  org(abc) -> a         dest(abc) -> b              apex(abc) -> c         */
+/*                                                                           */
+/*  bond:  Bond two triangles together at the resepective handles.           */
+/*  bond(abc, bad)                                                           */
+/*                                                                           */
+/*                                                                           */
+/*  For subsegments:                                                         */
+/*                                                                           */
+/*  ssym:  Reverse the orientation of a subsegment.                          */
+/*  ssym(ab) -> ba                                                           */
+/*                                                                           */
+/*  spivot:  Find adjoining subsegment with the same origin.                 */
+/*  spivot(ab) -> a*                                                         */
+/*                                                                           */
+/*  snext:  Find next subsegment in sequence.                                */
+/*  snext(ab) -> b*                                                          */
+/*                                                                           */
+/*  sorg:  Origin                      sdest:  Destination                   */
+/*  sorg(ab) -> a                      sdest(ab) -> b                        */
+/*                                                                           */
+/*  sbond:  Bond two subsegments together at the respective origins.         */
+/*  sbond(ab, ac)                                                            */
+/*                                                                           */
+/*                                                                           */
+/*  For interacting tetrahedra and subfacets:                                */
+/*                                                                           */
+/*  tspivot:  Find a subsegment abutting a triangle.                         */
+/*  tspivot(abc) -> ba                                                       */
+/*                                                                           */
+/*  stpivot:  Find a triangle abutting a subsegment.                         */
+/*  stpivot(ab) -> ba*                                                       */
+/*                                                                           */
+/*  tsbond:  Bond a triangle to a subsegment.                                */
+/*  tsbond(abc, ba)                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/********* Mesh manipulation primitives begin here                   *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/* Fast lookup arrays to speed some of the mesh manipulation primitives.     */
+
+int plus1mod3[3] = {1, 2, 0};
+int minus1mod3[3] = {2, 0, 1};
+
+/********* Primitives for triangles                                  *********/
+/*                                                                           */
+/*                                                                           */
+
+/* decode() converts a pointer to an oriented triangle.  The orientation is  */
+/*   extracted from the two least significant bits of the pointer.           */
+
+#define decode(ptr, otri)                                                     \
+  (otri).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l);         \
+  (otri).tri = (triangle *)                                                   \
+                  ((unsigned long) (ptr) ^ (unsigned long) (otri).orient)
+
+/* encode() compresses an oriented triangle into a single pointer.  It       */
+/*   relies on the assumption that all triangles are aligned to four-byte    */
+/*   boundaries, so the two least significant bits of (otri).tri are zero.   */
+
+#define encode(otri)                                                          \
+  (triangle) ((unsigned long) (otri).tri | (unsigned long) (otri).orient)
+
+/* The following handle manipulation primitives are all described by Guibas  */
+/*   and Stolfi.  However, Guibas and Stolfi use an edge-based data          */
+/*   structure, whereas I use a triangle-based data structure.               */
+
+/* sym() finds the abutting triangle, on the same edge.  Note that the edge  */
+/*   direction is necessarily reversed, because the handle specified by an   */
+/*   oriented triangle is directed counterclockwise around the triangle.     */
+
+#define sym(otri1, otri2)                                                     \
+  ptr = (otri1).tri[(otri1).orient];                                          \
+  decode(ptr, otri2);
+
+#define symself(otri)                                                         \
+  ptr = (otri).tri[(otri).orient];                                            \
+  decode(ptr, otri);
+
+/* lnext() finds the next edge (counterclockwise) of a triangle.             */
+
+#define lnext(otri1, otri2)                                                   \
+  (otri2).tri = (otri1).tri;                                                  \
+  (otri2).orient = plus1mod3[(otri1).orient]
+
+#define lnextself(otri)                                                       \
+  (otri).orient = plus1mod3[(otri).orient]
+
+/* lprev() finds the previous edge (clockwise) of a triangle.                */
+
+#define lprev(otri1, otri2)                                                   \
+  (otri2).tri = (otri1).tri;                                                  \
+  (otri2).orient = minus1mod3[(otri1).orient]
+
+#define lprevself(otri)                                                       \
+  (otri).orient = minus1mod3[(otri).orient]
+
+/* onext() spins counterclockwise around a vertex; that is, it finds the     */
+/*   next edge with the same origin in the counterclockwise direction.  This */
+/*   edge is part of a different triangle.                                   */
+
+#define onext(otri1, otri2)                                                   \
+  lprev(otri1, otri2);                                                        \
+  symself(otri2);
+
+#define onextself(otri)                                                       \
+  lprevself(otri);                                                            \
+  symself(otri);
+
+/* oprev() spins clockwise around a vertex; that is, it finds the next edge  */
+/*   with the same origin in the clockwise direction.  This edge is part of  */
+/*   a different triangle.                                                   */
+
+#define oprev(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lnextself(otri2);
+
+#define oprevself(otri)                                                       \
+  symself(otri);                                                              \
+  lnextself(otri);
+
+/* dnext() spins counterclockwise around a vertex; that is, it finds the     */
+/*   next edge with the same destination in the counterclockwise direction.  */
+/*   This edge is part of a different triangle.                              */
+
+#define dnext(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lprevself(otri2);
+
+#define dnextself(otri)                                                       \
+  symself(otri);                                                              \
+  lprevself(otri);
+
+/* dprev() spins clockwise around a vertex; that is, it finds the next edge  */
+/*   with the same destination in the clockwise direction.  This edge is     */
+/*   part of a different triangle.                                           */
+
+#define dprev(otri1, otri2)                                                   \
+  lnext(otri1, otri2);                                                        \
+  symself(otri2);
+
+#define dprevself(otri)                                                       \
+  lnextself(otri);                                                            \
+  symself(otri);
+
+/* rnext() moves one edge counterclockwise about the adjacent triangle.      */
+/*   (It's best understood by reading Guibas and Stolfi.  It involves        */
+/*   changing triangles twice.)                                              */
+
+#define rnext(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lnextself(otri2);                                                           \
+  symself(otri2);
+
+#define rnextself(otri)                                                       \
+  symself(otri);                                                              \
+  lnextself(otri);                                                            \
+  symself(otri);
+
+/* rprev() moves one edge clockwise about the adjacent triangle.             */
+/*   (It's best understood by reading Guibas and Stolfi.  It involves        */
+/*   changing triangles twice.)                                              */
+
+#define rprev(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lprevself(otri2);                                                           \
+  symself(otri2);
+
+#define rprevself(otri)                                                       \
+  symself(otri);                                                              \
+  lprevself(otri);                                                            \
+  symself(otri);
+
+/* These primitives determine or set the origin, destination, or apex of a   */
+/* triangle.                                                                 */
+
+#define org(otri, vertexptr)                                                  \
+  vertexptr = (vertex) (otri).tri[plus1mod3[(otri).orient] + 3]
+
+#define dest(otri, vertexptr)                                                 \
+  vertexptr = (vertex) (otri).tri[minus1mod3[(otri).orient] + 3]
+
+#define apex(otri, vertexptr)                                                 \
+  vertexptr = (vertex) (otri).tri[(otri).orient + 3]
+
+#define setorg(otri, vertexptr)                                               \
+  (otri).tri[plus1mod3[(otri).orient] + 3] = (triangle) vertexptr
+
+#define setdest(otri, vertexptr)                                              \
+  (otri).tri[minus1mod3[(otri).orient] + 3] = (triangle) vertexptr
+
+#define setapex(otri, vertexptr)                                              \
+  (otri).tri[(otri).orient + 3] = (triangle) vertexptr
+
+/* Bond two triangles together.                                              */
+
+#define bond(otri1, otri2)                                                    \
+  (otri1).tri[(otri1).orient] = encode(otri2);                                \
+  (otri2).tri[(otri2).orient] = encode(otri1)
+
+/* Dissolve a bond (from one side).  Note that the other triangle will still */
+/*   think it's connected to this triangle.  Usually, however, the other     */
+/*   triangle is being deleted entirely, or bonded to another triangle, so   */
+/*   it doesn't matter.                                                      */
+
+#define dissolve(otri)                                                        \
+  (otri).tri[(otri).orient] = (triangle) m->dummytri
+
+/* Copy an oriented triangle.                                                */
+
+#define otricopy(otri1, otri2)                                                \
+  (otri2).tri = (otri1).tri;                                                  \
+  (otri2).orient = (otri1).orient
+
+/* Test for equality of oriented triangles.                                  */
+
+#define otriequal(otri1, otri2)                                               \
+  (((otri1).tri == (otri2).tri) &&                                            \
+   ((otri1).orient == (otri2).orient))
+
+/* Primitives to infect or cure a triangle with the virus.  These rely on    */
+/*   the assumption that all subsegments are aligned to four-byte boundaries.*/
+
+#define infect(otri)                                                          \
+  (otri).tri[6] = (triangle)                                                  \
+                    ((unsigned long) (otri).tri[6] | (unsigned long) 2l)
+
+#define uninfect(otri)                                                        \
+  (otri).tri[6] = (triangle)                                                  \
+                    ((unsigned long) (otri).tri[6] & ~ (unsigned long) 2l)
+
+/* Test a triangle for viral infection.                                      */
+
+#define infected(otri)                                                        \
+  (((unsigned long) (otri).tri[6] & (unsigned long) 2l) != 0l)
+
+/* Check or set a triangle's attributes.                                     */
+
+#define elemattribute(otri, attnum)                                           \
+  ((REAL *) (otri).tri)[m->elemattribindex + (attnum)]
+
+#define setelemattribute(otri, attnum, value)                                 \
+  ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] = value
+
+/* Check or set a triangle's maximum area bound.                             */
+
+#define areabound(otri)  ((REAL *) (otri).tri)[m->areaboundindex]
+
+#define setareabound(otri, value)                                             \
+  ((REAL *) (otri).tri)[m->areaboundindex] = value
+
+/* Check or set a triangle's deallocation.  Its second pointer is set to     */
+/*   NULL to indicate that it is not allocated.  (Its first pointer is used  */
+/*   for the stack of dead items.)  Its fourth pointer (its first vertex)    */
+/*   is set to NULL in case a `badtriang' structure points to it.            */
+
+#define deadtri(tria)  ((tria)[1] == (triangle) NULL)
+
+#define killtri(tria)                                                         \
+  (tria)[1] = (triangle) NULL;                                                \
+  (tria)[3] = (triangle) NULL
+
+/********* Primitives for subsegments                                *********/
+/*                                                                           */
+/*                                                                           */
+
+/* sdecode() converts a pointer to an oriented subsegment.  The orientation  */
+/*   is extracted from the least significant bit of the pointer.  The two    */
+/*   least significant bits (one for orientation, one for viral infection)   */
+/*   are masked out to produce the real pointer.                             */
+
+#define sdecode(sptr, osub)                                                   \
+  (osub).ssorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l);      \
+  (osub).ss = (subseg *)                                                      \
+              ((unsigned long) (sptr) & ~ (unsigned long) 3l)
+
+/* sencode() compresses an oriented subsegment into a single pointer.  It    */
+/*   relies on the assumption that all subsegments are aligned to two-byte   */
+/*   boundaries, so the least significant bit of (osub).ss is zero.          */
+
+#define sencode(osub)                                                         \
+  (subseg) ((unsigned long) (osub).ss | (unsigned long) (osub).ssorient)
+
+/* ssym() toggles the orientation of a subsegment.                           */
+
+#define ssym(osub1, osub2)                                                    \
+  (osub2).ss = (osub1).ss;                                                    \
+  (osub2).ssorient = 1 - (osub1).ssorient
+
+#define ssymself(osub)                                                        \
+  (osub).ssorient = 1 - (osub).ssorient
+
+/* spivot() finds the other subsegment (from the same segment) that shares   */
+/*   the same origin.                                                        */
+
+#define spivot(osub1, osub2)                                                  \
+  sptr = (osub1).ss[(osub1).ssorient];                                        \
+  sdecode(sptr, osub2)
+
+#define spivotself(osub)                                                      \
+  sptr = (osub).ss[(osub).ssorient];                                          \
+  sdecode(sptr, osub)
+
+/* snext() finds the next subsegment (from the same segment) in sequence;    */
+/*   one whose origin is the input subsegment's destination.                 */
+
+#define snext(osub1, osub2)                                                   \
+  sptr = (osub1).ss[1 - (osub1).ssorient];                                    \
+  sdecode(sptr, osub2)
+
+#define snextself(osub)                                                       \
+  sptr = (osub).ss[1 - (osub).ssorient];                                      \
+  sdecode(sptr, osub)
+
+/* These primitives determine or set the origin or destination of a          */
+/*   subsegment.                                                             */
+
+#define sorg(osub, vertexptr)                                                 \
+  vertexptr = (vertex) (osub).ss[2 + (osub).ssorient]
+
+#define sdest(osub, vertexptr)                                                \
+  vertexptr = (vertex) (osub).ss[3 - (osub).ssorient]
+
+#define setsorg(osub, vertexptr)                                              \
+  (osub).ss[2 + (osub).ssorient] = (subseg) vertexptr
+
+#define setsdest(osub, vertexptr)                                             \
+  (osub).ss[3 - (osub).ssorient] = (subseg) vertexptr
+
+/* These primitives read or set a boundary marker.  Boundary markers are     */
+/*   used to hold user-defined tags for setting boundary conditions in       */
+/*   finite element solvers.                                                 */
+
+#define mark(osub)  (* (int *) ((osub).ss + 6))
+
+#define setmark(osub, value)                                                  \
+  * (int *) ((osub).ss + 6) = value
+
+/* Bond two subsegments together.                                            */
+
+#define sbond(osub1, osub2)                                                   \
+  (osub1).ss[(osub1).ssorient] = sencode(osub2);                              \
+  (osub2).ss[(osub2).ssorient] = sencode(osub1)
+
+/* Dissolve a subsegment bond (from one side).  Note that the other          */
+/*   subsegment will still think it's connected to this subsegment.          */
+
+#define sdissolve(osub)                                                       \
+  (osub).ss[(osub).ssorient] = (subseg) m->dummysub
+
+/* Copy a subsegment.                                                        */
+
+#define subsegcopy(osub1, osub2)                                              \
+  (osub2).ss = (osub1).ss;                                                    \
+  (osub2).ssorient = (osub1).ssorient
+
+/* Test for equality of subsegments.                                         */
+
+#define subsegequal(osub1, osub2)                                             \
+  (((osub1).ss == (osub2).ss) &&                                              \
+   ((osub1).ssorient == (osub2).ssorient))
+
+/* Check or set a subsegment's deallocation.  Its second pointer is set to   */
+/*   NULL to indicate that it is not allocated.  (Its first pointer is used  */
+/*   for the stack of dead items.)  Its third pointer (its first vertex)     */
+/*   is set to NULL in case a `badsubseg' structure points to it.            */
+
+#define deadsubseg(sub)  ((sub)[1] == (subseg) NULL)
+
+#define killsubseg(sub)                                                       \
+  (sub)[1] = (subseg) NULL;                                                   \
+  (sub)[2] = (subseg) NULL
+
+/********* Primitives for interacting triangles and subsegments      *********/
+/*                                                                           */
+/*                                                                           */
+
+/* tspivot() finds a subsegment abutting a triangle.                         */
+
+#define tspivot(otri, osub)                                                   \
+  sptr = (subseg) (otri).tri[6 + (otri).orient];                              \
+  sdecode(sptr, osub)
+
+/* stpivot() finds a triangle abutting a subsegment.  It requires that the   */
+/*   variable `ptr' of type `triangle' be defined.                           */
+
+#define stpivot(osub, otri)                                                   \
+  ptr = (triangle) (osub).ss[4 + (osub).ssorient];                            \
+  decode(ptr, otri)
+
+/* Bond a triangle to a subsegment.                                          */
+
+#define tsbond(otri, osub)                                                    \
+  (otri).tri[6 + (otri).orient] = (triangle) sencode(osub);                   \
+  (osub).ss[4 + (osub).ssorient] = (subseg) encode(otri)
+
+/* Dissolve a bond (from the triangle side).                                 */
+
+#define tsdissolve(otri)                                                      \
+  (otri).tri[6 + (otri).orient] = (triangle) m->dummysub
+
+/* Dissolve a bond (from the subsegment side).                               */
+
+#define stdissolve(osub)                                                      \
+  (osub).ss[4 + (osub).ssorient] = (subseg) m->dummytri
+
+/********* Primitives for vertices                                   *********/
+/*                                                                           */
+/*                                                                           */
+
+#define vertexmark(vx)  ((int *) (vx))[m->vertexmarkindex]
+
+#define setvertexmark(vx, value)                                              \
+  ((int *) (vx))[m->vertexmarkindex] = value
+
+#define vertextype(vx)  ((int *) (vx))[m->vertexmarkindex + 1]
+
+#define setvertextype(vx, value)                                              \
+  ((int *) (vx))[m->vertexmarkindex + 1] = value
+
+#define vertex2tri(vx)  ((triangle *) (vx))[m->vertex2triindex]
+
+#define setvertex2tri(vx, value)                                              \
+  ((triangle *) (vx))[m->vertex2triindex] = value
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh manipulation primitives end here                     *********/
+
+/********* User-defined triangle evaluation routine begins here      *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triunsuitable()   Determine if a triangle is unsuitable, and thus must   */
+/*                    be further refined.                                    */
+/*                                                                           */
+/*  You may write your own procedure that decides whether or not a selected  */
+/*  triangle is too big (and needs to be refined).  There are two ways to do */
+/*  this.                                                                    */
+/*                                                                           */
+/*  (1)  Modify the procedure `triunsuitable' below, then recompile          */
+/*  Triangle.                                                                */
+/*                                                                           */
+/*  (2)  Define the symbol EXTERNAL_TEST (either by adding the definition    */
+/*  to this file, or by using the appropriate compiler switch).  This way,   */
+/*  you can compile triangle.c separately from your test.  Write your own    */
+/*  `triunsuitable' procedure in a separate C file (using the same prototype */
+/*  as below).  Compile it and link the object code with triangle.o.         */
+/*                                                                           */
+/*  This procedure returns 1 if the triangle is too large and should be      */
+/*  refined; 0 otherwise.                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef EXTERNAL_TEST
+
+int triunsuitable();
+
+#else /* not EXTERNAL_TEST */
+
+int triunsuitable(triorg, tridest, triapex, area)
+vertex triorg;                              /* The triangle's origin vertex. */
+vertex tridest;                        /* The triangle's destination vertex. */
+vertex triapex;                               /* The triangle's apex vertex. */
+REAL area;                                      /* The area of the triangle. */
+{
+  REAL dxoa, dxda, dxod;
+  REAL dyoa, dyda, dyod;
+  REAL oalen, dalen, odlen;
+  REAL maxlen;
+
+  dxoa = triorg[0] - triapex[0];
+  dyoa = triorg[1] - triapex[1];
+  dxda = tridest[0] - triapex[0];
+  dyda = tridest[1] - triapex[1];
+  dxod = triorg[0] - tridest[0];
+  dyod = triorg[1] - tridest[1];
+  /* Find the squares of the lengths of the triangle's three edges. */
+  oalen = dxoa * dxoa + dyoa * dyoa;
+  dalen = dxda * dxda + dyda * dyda;
+  odlen = dxod * dxod + dyod * dyod;
+  /* Find the square of the length of the longest edge. */
+  maxlen = (dalen > oalen) ? dalen : oalen;
+  maxlen = (odlen > maxlen) ? odlen : maxlen;
+
+  if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+#endif /* not EXTERNAL_TEST */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* User-defined triangle evaluation routine ends here        *********/
+
+/********* Memory allocation wrappers begin here                     *********/
+/**                                                                         **/
+/**                                                                         **/
+
+#ifdef ANSI_DECLARATORS
+VOID *trimalloc(int size)
+#else /* not ANSI_DECLARATORS */
+VOID *trimalloc(size)
+int size;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID *memptr;
+
+  memptr = malloc((unsigned int) size);
+  if (memptr == (VOID *) NULL) {
+    printf("Error:  Out of memory.\n");
+    exit(1);
+  }
+  return(memptr);
+}
+
+#ifdef ANSI_DECLARATORS
+void trifree(VOID *memptr)
+#else /* not ANSI_DECLARATORS */
+void trifree(memptr)
+VOID *memptr;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  free(memptr);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Memory allocation wrappers end here                       *********/
+
+/********* User interaction routines begin here                      *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  syntax()   Print list of command line switches.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+void syntax()
+{
+#ifdef CDT_ONLY
+#ifdef REDUCED
+  printf("triangle [-pAcjevngBPNEIOXzo_lQVh] input_file\n");
+#else /* not REDUCED */
+  printf("triangle [-pAcjevngBPNEIOXzo_iFlCQVh] input_file\n");
+#endif /* not REDUCED */
+#else /* not CDT_ONLY */
+#ifdef REDUCED
+  printf("triangle [-prq__a__uAcjevngBPNEIOXzo_YS__LlQVh] input_file\n");
+#else /* not REDUCED */
+  printf("triangle [-prq__a__uAcjevngBPNEIOXzo_YS__LiFlsCQVh] input_file\n");
+#endif /* not REDUCED */
+#endif /* not CDT_ONLY */
+
+  printf("    -p  Triangulates a Planar Straight Line Graph (.poly file).\n");
+#ifndef CDT_ONLY
+  printf("    -r  Refines a previously generated mesh.\n");
+  printf(
+    "    -q  Quality mesh generation.  A minimum angle may be specified.\n");
+  printf("    -a  Applies a maximum triangle area constraint.\n");
+  printf("    -u  Applies a user-defined triangle constraint.\n");
+#endif /* not CDT_ONLY */
+  printf(
+    "    -A  Applies attributes to identify triangles in certain regions.\n");
+  printf("    -c  Encloses the convex hull with segments.\n");
+  printf("    -w  Weighted Delaunay triangulation.\n");
+  printf("    -W  Regular triangulation (lower hull of a height field).\n");
+  printf("    -j  Jettison unused vertices from output .node file.\n");
+  printf("    -e  Generates an edge list.\n");
+  printf("    -v  Generates a Voronoi diagram.\n");
+  printf("    -n  Generates a list of triangle neighbors.\n");
+  printf("    -g  Generates an .off file for Geomview.\n");
+  printf("    -B  Suppresses output of boundary information.\n");
+  printf("    -P  Suppresses output of .poly file.\n");
+  printf("    -N  Suppresses output of .node file.\n");
+  printf("    -E  Suppresses output of .ele file.\n");
+  printf("    -I  Suppresses mesh iteration numbers.\n");
+  printf("    -O  Ignores holes in .poly file.\n");
+  printf("    -X  Suppresses use of exact arithmetic.\n");
+  printf("    -z  Numbers all items starting from zero (rather than one).\n");
+  printf("    -o2 Generates second-order subparametric elements.\n");
+#ifndef CDT_ONLY
+  printf("    -Y  Suppresses boundary segment splitting.\n");
+  printf("    -S  Specifies maximum number of added Steiner points.\n");
+  printf("    -L  Uses equatorial circles, not equatorial lenses.\n");
+#endif /* not CDT_ONLY */
+#ifndef REDUCED
+  printf("    -i  Uses incremental method, rather than divide-and-conquer.\n");
+  printf("    -F  Uses Fortune's sweepline algorithm, rather than d-and-c.\n");
+#endif /* not REDUCED */
+  printf("    -l  Uses vertical cuts only, rather than alternating cuts.\n");
+#ifndef REDUCED
+#ifndef CDT_ONLY
+  printf(
+    "    -s  Force segments into mesh by splitting (instead of using CDT).\n");
+  printf("    -L  Uses Ruppert's diametral spheres, not diametral lenses.\n");
+#endif /* not CDT_ONLY */
+  printf("    -C  Check consistency of final mesh.\n");
+#endif /* not REDUCED */
+  printf("    -Q  Quiet:  No terminal output except errors.\n");
+  printf("    -V  Verbose:  Detailed information on what I'm doing.\n");
+  printf("    -h  Help:  Detailed instructions for Triangle.\n");
+  exit(0);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  info()   Print out complete instructions.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+void info()
+{
+  printf("Triangle\n");
+  printf(
+"A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.\n");
+  printf("Version 1.5\n\n");
+  printf(
+"Copyright 1993, 1995, 1997, 1998, 2002, 2004 Jonathan Richard Shewchuk\n");
+  printf("2360 Woolsey #H / Berkeley, California 94705-1927\n");
+  printf("Bugs/comments to jrs@cs.berkeley.edu\n");
+  printf(
+"Created as part of the Archimedes project (tools for parallel FEM).\n");
+  printf(
+"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n");
+  printf("There is no warranty whatsoever.  Use at your own risk.\n");
+#ifdef SINGLE
+  printf("This executable is compiled for single precision arithmetic.\n\n\n");
+#else /* not SINGLE */
+  printf("This executable is compiled for double precision arithmetic.\n\n\n");
+#endif /* not SINGLE */
+  printf(
+"Triangle generates exact Delaunay triangulations, constrained Delaunay\n");
+  printf(
+"triangulations, Voronoi diagrams, and quality conforming Delaunay\n");
+  printf(
+"triangulations.  The latter can be generated with no small angles, and are\n"
+);
+  printf(
+"thus suitable for finite element analysis.  If no command line switches are\n"
+);
+  printf(
+"specified, your .node input file is read, and the Delaunay triangulation is\n"
+);
+  printf("returned in .node and .ele output files.  The command syntax is:\n");
+  printf("\n");
+  printf("triangle [-prq__a__uAcjevngBPNEIOXzo_YS__LiFlsCQVh] input_file\n");
+  printf("\n");
+  printf(
+"Underscores indicate that numbers may optionally follow certain switches.\n");
+  printf(
+"Do not leave any space between a switch and its numeric parameter.\n");
+  printf(
+"input_file must be a file with extension .node, or extension .poly if the\n");
+  printf(
+"-p switch is used.  If -r is used, you must supply .node and .ele files,\n");
+  printf(
+"and possibly a .poly file and an .area file as well.  The formats of these\n"
+);
+  printf("files are described below.\n\n");
+  printf("Command Line Switches:\n\n");
+  printf(
+"    -p  Reads a Planar Straight Line Graph (.poly file), which can specify\n"
+);
+  printf("        vertices, segments, holes, regional attributes, and area\n");
+  printf(
+"        constraints.  Generates a constrained Delaunay triangulation (CDT)\n"
+);
+  printf(
+"        fitting the input; or, if -s, -q, -a, or -u is used, a conforming\n");
+  printf(
+"        constrained Delaunay triangulation (CCDT).  If -p is not used,\n");
+  printf("        Triangle reads a .node file by default.\n");
+  printf(
+"    -r  Refines a previously generated mesh.  The mesh is read from a .node\n"
+);
+  printf(
+"        file and an .ele file.  If -p is also used, a .poly file is read\n");
+  printf(
+"        and used to constrain segments in the mesh.  If -a is also used\n");
+  printf(
+"        (with no number following), an .area file is read and used to\n");
+  printf(
+"        impose area constraints on the mesh.  Further details on refinement\n"
+);
+  printf("        are given below.\n");
+  printf(
+"    -q  Quality mesh generation by my variant of Jim Ruppert's Delaunay\n");
+  printf(
+"        refinement algorithm.  Adds vertices to the mesh to ensure that no\n"
+);
+  printf(
+"        angles smaller than 20 degrees occur.  An alternative minimum angle\n"
+);
+  printf(
+"        may be specified after the `q'.  If the minimum angle is 20.7\n");
+  printf(
+"        degrees or smaller, the triangulation algorithm is mathematically\n");
+  printf(
+"        guaranteed to terminate (assuming infinite precision arithmetic--\n");
+  printf(
+"        Triangle may fail to terminate if you run out of precision).  In\n");
+  printf(
+"        practice, the algorithm often succeeds for minimum angles up to\n");
+  printf(
+"        33.8 degrees.  For some meshes, however, it may be necessary to\n");
+  printf(
+"        reduce the minimum angle to avoid problems associated with\n");
+  printf(
+"        insufficient floating-point precision.  The specified angle may\n");
+  printf("        include a decimal point.\n");
+  printf(
+"    -a  Imposes a maximum triangle area.  If a number follows the `a', no\n");
+  printf(
+"        triangle is generated whose area is larger than that number.  If no\n"
+);
+  printf(
+"        number is specified, an .area file (if -r is used) or .poly file\n");
+  printf(
+"        (if -r is not used) specifies a set of maximum area constraints.\n");
+  printf(
+"        An .area file contains a separate area constraint for each\n");
+  printf(
+"        triangle, and is useful for refining a finite element mesh based on\n"
+);
+  printf(
+"        a posteriori error estimates.  A .poly file can optionally contain\n"
+);
+  printf(
+"        an area constraint for each segment-bounded region, thereby\n");
+  printf(
+"        controlling triangle densities in a first triangulation of a PSLG.\n"
+);
+  printf(
+"        You can impose both a fixed area constraint and a varying area\n");
+  printf(
+"        constraint by invoking the -a switch twice, once with and once\n");
+  printf(
+"        without a number following.  Each area specified may include a\n");
+  printf("        decimal point.\n");
+  printf(
+"    -u  Imposes a user-defined constraint on triangle size.  There are two\n"
+);
+  printf(
+"        ways to use this feature.  One is to edit the triunsuitable()\n");
+  printf(
+"        procedure in triangle.c to encode any constraint you like, then\n");
+  printf(
+"        recompile Triangle.  The other is to compile triangle.c with the\n");
+  printf(
+"        EXTERNAL_TEST symbol set (compiler switch -DEXTERNAL_TEST), then\n");
+  printf(
+"        link Triangle against a separate object file that implements\n");
+  printf(
+"        triunsuitable().  In either case, the -u switch causes the user-\n");
+  printf("        defined test to be applied to every triangle.\n");
+  printf(
+"    -A  Assigns an additional attribute to each triangle that identifies\n");
+  printf(
+"        what segment-bounded region each triangle belongs to.  Attributes\n");
+  printf(
+"        are assigned to regions by the .poly file.  If a region is not\n");
+  printf(
+"        explicitly marked by the .poly file, triangles in that region are\n");
+  printf(
+"        assigned an attribute of zero.  The -A switch has an effect only\n");
+  printf("        when the -p switch is used and the -r switch is not.\n");
+  printf(
+"    -c  Creates segments on the convex hull of the triangulation.  If you\n");
+  printf(
+"        are triangulating a vertex set, this switch causes a .poly file to\n"
+);
+  printf(
+"        be written, containing all edges in the convex hull.  If you are\n");
+  printf(
+"        triangulating a PSLG, this switch specifies that the whole convex\n");
+  printf(
+"        hull of the PSLG should be triangulated, regardless of what\n");
+  printf(
+"        segments the PSLG has.  If you do not use this switch when\n");
+  printf(
+"        triangulating a PSLG, it is assumed that you have identified the\n");
+  printf(
+"        region to be triangulated by surrounding it with segments of the\n");
+  printf(
+"        input PSLG.  Beware:  if you are not careful, this switch can cause\n"
+);
+  printf(
+"        the introduction of an extremely thin angle between a PSLG segment\n"
+);
+  printf(
+"        and a convex hull segment, which can cause overrefinement (and\n");
+  printf(
+"        possibly failure if Triangle runs out of precision).  If you are\n");
+  printf(
+"        refining a mesh, the -c switch works differently; it generates the\n"
+);
+  printf(
+"        set of boundary edges of the mesh (useful if no .poly file was\n");
+  printf("        read).\n");
+  printf(
+"    -j  Jettisons vertices that are not part of the final triangulation\n");
+  printf(
+"        from the output .node file.  By default, Triangle copies all\n");
+  printf(
+"        vertices in the input .node file to the output .node file, in the\n");
+  printf(
+"        same order, so their indices do not change.  The -j switch prevents\n"
+);
+  printf(
+"        duplicated input vertices from appearing in the output .node file;\n"
+);
+  printf(
+"        hence, if two input vertices have exactly the same coordinates,\n");
+  printf(
+"        only the first appears in the output.  If any vertices are\n");
+  printf(
+"        jettisoned, the vertex numbering in the output .node file differs\n");
+  printf("        from that of the input .node file.\n");
+  printf(
+"    -e  Outputs (to an .edge file) a list of edges of the triangulation.\n");
+  printf(
+"    -v  Outputs the Voronoi diagram associated with the triangulation.\n");
+  printf(
+"        Does not attempt to detect degeneracies, so some Voronoi vertices\n");
+  printf(
+"        may be duplicated.  See the discussion of Voronoi diagrams below.\n");
+  printf(
+"    -n  Outputs (to a .neigh file) a list of triangles neighboring each\n");
+  printf("        triangle.\n");
+  printf(
+"    -g  Outputs the mesh to an Object File Format (.off) file, suitable for\n"
+);
+  printf("        viewing with the Geometry Center's Geomview package.\n");
+  printf(
+"    -B  No boundary markers in the output .node, .poly, and .edge output\n");
+  printf(
+"        files.  See the detailed discussion of boundary markers below.\n");
+  printf(
+"    -P  No output .poly file.  Saves disk space, but you lose the ability\n");
+  printf(
+"        to maintain constraining segments on later refinements of the mesh.\n"
+);
+  printf("    -N  No output .node file.\n");
+  printf("    -E  No output .ele file.\n");
+  printf(
+"    -I  No iteration numbers.  Suppresses the output of .node and .poly\n");
+  printf(
+"        files, so your input files won't be overwritten.  (If your input is\n"
+);
+  printf(
+"        a .poly file only, a .node file is written.)  Cannot be used with\n");
+  printf(
+"        the -r switch, because that would overwrite your input .ele file.\n");
+  printf(
+"        Shouldn't be used with the -q, -a, -u, or -s switch if you are\n");
+  printf(
+"        using a .node file for input, because no .node file is written, so\n"
+);
+  printf("        there is no record of any added Steiner points.\n");
+  printf("    -O  No holes.  Ignores the holes in the .poly file.\n");
+  printf(
+"    -X  No exact arithmetic.  Normally, Triangle uses exact floating-point\n"
+);
+  printf(
+"        arithmetic for certain tests if it thinks the inexact tests are not\n"
+);
+  printf(
+"        accurate enough.  Exact arithmetic ensures the robustness of the\n");
+  printf(
+"        triangulation algorithms, despite floating-point roundoff error.\n");
+  printf(
+"        Disabling exact arithmetic with the -X switch causes a small\n");
+  printf(
+"        improvement in speed and creates the possibility (albeit small)\n");
+  printf(
+"        that Triangle will fail to produce a valid mesh.  Not recommended.\n"
+);
+  printf(
+"    -z  Numbers all items starting from zero (rather than one).  Note that\n"
+);
+  printf(
+"        this switch is normally overrided by the value used to number the\n");
+  printf(
+"        first vertex of the input .node or .poly file.  However, this\n");
+  printf(
+"        switch is useful when calling Triangle from another program.\n");
+  printf(
+"    -o2 Generates second-order subparametric elements with six nodes each.\n"
+);
+  printf(
+"    -Y  No new vertices on the boundary.  This switch is useful when the\n");
+  printf(
+"        mesh boundary must be preserved so that it conforms to some\n");
+  printf(
+"        adjacent mesh.  Be forewarned that you will probably sacrifice some\n"
+);
+  printf(
+"        of the quality of the mesh; Triangle will try, but the resulting\n");
+  printf(
+"        mesh may contain triangles of poor aspect ratio.  Works well if all\n"
+);
+  printf(
+"        the boundary vertices are closely spaced.  Specify this switch\n");
+  printf(
+"        twice (`-YY') to prevent all segment splitting, including internal\n"
+);
+  printf("        boundaries.\n");
+  printf(
+"    -S  Specifies the maximum number of Steiner points (vertices that are\n");
+  printf(
+"        not in the input, but are added to meet the constraints on minimum\n"
+);
+  printf(
+"        angle and maximum area).  The default is to allow an unlimited\n");
+  printf(
+"        number.  If you specify this switch with no number after it,\n");
+  printf(
+"        the limit is set to zero.  Triangle always adds vertices at segment\n"
+);
+  printf(
+"        intersections, even if it needs to use more vertices than the limit\n"
+);
+  printf(
+"        you set.  When Triangle inserts segments by splitting (-s), it\n");
+  printf(
+"        always adds enough vertices to ensure that all the segments of the\n"
+);
+  printf("        PLSG are recovered, ignoring the limit if necessary.\n");
+  printf(
+"    -L  Do not use diametral lenses to determine whether subsegments are\n");
+  printf(
+"        encroached; use diametral circles instead (as in Ruppert's\n");
+  printf(
+"        algorithm).  Use this switch if you want all triangles in the mesh\n"
+);
+  printf(
+"        to be Delaunay, and not just constrained Delaunay; or if you want\n");
+  printf(
+"        to ensure that all Voronoi vertices lie within the triangulation.\n");
+  printf(
+"        (Applications such as some finite volume methods may have this\n");
+  printf(
+"        requirement.)  This switch may increase the number of vertices in\n");
+  printf("        the mesh to meet these constraints.\n");
+  printf(
+"    -i  Uses an incremental rather than divide-and-conquer algorithm to\n");
+  printf(
+"        form a Delaunay triangulation.  Try it if the divide-and-conquer\n");
+  printf("        algorithm fails.\n");
+  printf(
+"    -F  Uses Steven Fortune's sweepline algorithm to form a Delaunay\n");
+  printf(
+"        triangulation.  Warning:  does not use exact arithmetic for all\n");
+  printf("        calculations.  An exact result is not guaranteed.\n");
+  printf(
+"    -l  Uses only vertical cuts in the divide-and-conquer algorithm.  By\n");
+  printf(
+"        default, Triangle uses alternating vertical and horizontal cuts,\n");
+  printf(
+"        which usually improve the speed except with vertex sets that are\n");
+  printf(
+"        small or short and wide.  This switch is primarily of theoretical\n");
+  printf("        interest.\n");
+  printf(
+"    -s  Specifies that segments should be forced into the triangulation by\n"
+);
+  printf(
+"        recursively splitting them at their midpoints, rather than by\n");
+  printf(
+"        generating a constrained Delaunay triangulation.  Segment splitting\n"
+);
+  printf(
+"        is true to Ruppert's original algorithm, but can create needlessly\n"
+);
+  printf(
+"        small triangles.  This switch is primarily of theoretical interest.\n"
+);
+  printf(
+"    -C  Check the consistency of the final mesh.  Uses exact arithmetic for\n"
+);
+  printf(
+"        checking, even if the -X switch is used.  Useful if you suspect\n");
+  printf("        Triangle is buggy.\n");
+  printf(
+"    -Q  Quiet:  Suppresses all explanation of what Triangle is doing,\n");
+  printf("        unless an error occurs.\n");
+  printf(
+"    -V  Verbose:  Gives detailed information about what Triangle is doing.\n"
+);
+  printf(
+"        Add more `V's for increasing amount of detail.  `-V' gives\n");
+  printf(
+"        information on algorithmic progress and more detailed statistics.\n");
+  printf(
+"        `-VV' gives vertex-by-vertex details, and prints so much that\n");
+  printf(
+"        Triangle runs much more slowly.  `-VVVV' gives information only\n");
+  printf("        a debugger could love.\n");
+  printf("    -h  Help:  Displays these instructions.\n");
+  printf("\n");
+  printf("Definitions:\n");
+  printf("\n");
+  printf(
+"  A Delaunay triangulation of a vertex set is a triangulation whose\n");
+  printf(
+"  vertices are the vertex set, wherein no vertex in the vertex set falls in\n"
+);
+  printf(
+"  the interior of the circumcircle (circle that passes through all three\n");
+  printf("  vertices) of any triangle in the triangulation.\n\n");
+  printf(
+"  A Voronoi diagram of a vertex set is a subdivision of the plane into\n");
+  printf(
+"  polygonal regions (some of which may be infinite), where each region is\n");
+  printf(
+"  the set of points in the plane that are closer to some input vertex than\n"
+);
+  printf(
+"  to any other input vertex.  (The Voronoi diagram is the geometric dual of\n"
+);
+  printf("  the Delaunay triangulation.)\n\n");
+  printf(
+"  A Planar Straight Line Graph (PSLG) is a set of vertices and segments.\n");
+  printf(
+"  Segments are simply edges, whose endpoints are vertices in the PSLG.\n");
+  printf(
+"  Segments may intersect each other only at their endpoints.  The file\n");
+  printf("  format for PSLGs (.poly files) is described below.\n\n");
+  printf(
+"  A constrained Delaunay triangulation (CDT) of a PSLG is similar to a\n");
+  printf(
+"  Delaunay triangulation, but each PSLG segment is present as a single edge\n"
+);
+  printf(
+"  in the triangulation.  (A constrained Delaunay triangulation is not truly\n"
+);
+  printf(
+"  a Delaunay triangulation.)  By definition, a CDT does not have any\n");
+  printf("  vertices other than those specified in the input PSLG.\n\n");
+  printf(
+"  A conforming Delaunay triangulation of a PSLG is a true Delaunay\n");
+  printf(
+"  triangulation in which each PSLG segment is represented by a linear\n");
+  printf(
+"  contiguous sequence of edges in the triangulation.  Each input segment\n");
+  printf(
+"  may have been subdivided into shorter subsegments by the insertion of\n");
+  printf(
+"  additional vertices.  These inserted vertices are necessary to maintain\n");
+  printf(
+"  the Delaunay property while ensuring that every segment is represented.\n");
+  printf("\n");
+  printf("File Formats:\n");
+  printf("\n");
+  printf(
+"  All files may contain comments prefixed by the character '#'.  Vertices,\n"
+);
+  printf(
+"  triangles, edges, holes, and maximum area constraints must be numbered\n");
+  printf(
+"  consecutively, starting from either 1 or 0.  Whichever you choose, all\n");
+  printf(
+"  input files must be consistent; if the vertices are numbered from 1, so\n");
+  printf(
+"  must be all other objects.  Triangle automatically detects your choice\n");
+  printf(
+"  while reading the .node (or .poly) file.  (When calling Triangle from\n");
+  printf(
+"  another program, use the -z switch if you wish to number objects from\n");
+  printf("  zero.)  Examples of these file formats are given below.\n\n");
+  printf("  .node files:\n");
+  printf(
+"    First line:  <# of vertices> <dimension (must be 2)> <# of attributes>\n"
+);
+  printf(
+"                                           <# of boundary markers (0 or 1)>\n"
+);
+  printf(
+"    Remaining lines:  <vertex #> <x> <y> [attributes] [boundary marker]\n");
+  printf("\n");
+  printf(
+"    The attributes, which are typically floating-point values of physical\n");
+  printf(
+"    quantities (such as mass or conductivity) associated with the nodes of\n"
+);
+  printf(
+"    a finite element mesh, are copied unchanged to the output mesh.  If -q,\n"
+);
+  printf(
+"    -a, -u, or -s is selected, each new Steiner point added to the mesh\n");
+  printf("    has attributes assigned to it by linear interpolation.\n\n");
+  printf(
+"    If the fourth entry of the first line is `1', the last column of the\n");
+  printf(
+"    remainder of the file is assumed to contain boundary markers.  Boundary\n"
+);
+  printf(
+"    markers are used to identify boundary vertices and vertices resting on\n"
+);
+  printf(
+"    PSLG segments; a complete description appears in a section below.  The\n"
+);
+  printf(
+"    .node file produced by Triangle contains boundary markers in the last\n");
+  printf("    column unless they are suppressed by the -B switch.\n\n");
+  printf("  .ele files:\n");
+  printf(
+"    First line:  <# of triangles> <nodes per triangle> <# of attributes>\n");
+  printf(
+"    Remaining lines:  <triangle #> <node> <node> <node> ... [attributes]\n");
+  printf("\n");
+  printf(
+"    Nodes are indices into the corresponding .node file.  The first three\n");
+  printf(
+"    nodes are the corner vertices, and are listed in counterclockwise order\n"
+);
+  printf(
+"    around each triangle.  (The remaining nodes, if any, depend on the type\n"
+);
+  printf("    of finite element used.)\n\n");
+  printf(
+"    The attributes are just like those of .node files.  Because there is no\n"
+);
+  printf(
+"    simple mapping from input to output triangles, an attempt is made to\n");
+  printf(
+"    interpolate attributes, which may result in a good deal of diffusion of\n"
+);
+  printf(
+"    attributes among nearby triangles as the triangulation is refined.\n");
+  printf(
+"    Attributes do not diffuse across segments, so attributes used to\n");
+  printf("    identify segment-bounded regions remain intact.\n\n");
+  printf(
+"    In .ele files produced by Triangle, each triangular element has three\n");
+  printf(
+"    nodes (vertices) unless the -o2 switch is used, in which case\n");
+  printf(
+"    subparametric quadratic elements with six nodes each are generated.\n");
+  printf(
+"    The first three nodes are the corners in counterclockwise order, and\n");
+  printf(
+"    the fourth, fifth, and sixth nodes lie on the midpoints of the edges\n");
+  printf(
+"    opposite the first, second, and third vertices, respectively.\n");
+  printf("\n");
+  printf("  .poly files:\n");
+  printf(
+"    First line:  <# of vertices> <dimension (must be 2)> <# of attributes>\n"
+);
+  printf(
+"                                           <# of boundary markers (0 or 1)>\n"
+);
+  printf(
+"    Following lines:  <vertex #> <x> <y> [attributes] [boundary marker]\n");
+  printf("    One line:  <# of segments> <# of boundary markers (0 or 1)>\n");
+  printf(
+"    Following lines:  <segment #> <endpoint> <endpoint> [boundary marker]\n");
+  printf("    One line:  <# of holes>\n");
+  printf("    Following lines:  <hole #> <x> <y>\n");
+  printf(
+"    Optional line:  <# of regional attributes and/or area constraints>\n");
+  printf(
+"    Optional following lines:  <region #> <x> <y> <attribute> <max area>\n");
+  printf("\n");
+  printf(
+"    A .poly file represents a PSLG, as well as some additional information.\n"
+);
+  printf(
+"    The first section lists all the vertices, and is identical to the\n");
+  printf(
+"    format of .node files.  <# of vertices> may be set to zero to indicate\n"
+);
+  printf(
+"    that the vertices are listed in a separate .node file; .poly files\n");
+  printf(
+"    produced by Triangle always have this format.  A vertex set represented\n"
+);
+  printf(
+"    this way has the advantage that it may easily be triangulated with or\n");
+  printf(
+"    without segments (depending on whether the .poly or .node file is\n");
+  printf("    read).\n\n");
+  printf(
+"    The second section lists the segments.  Segments are edges whose\n");
+  printf(
+"    presence in the triangulation is enforced (although each segment may be\n"
+);
+  printf(
+"    subdivided into smaller edges).  Each segment is specified by listing\n");
+  printf(
+"    the indices of its two endpoints.  This means that you must include its\n"
+);
+  printf(
+"    endpoints in the vertex list.  Each segment, like each point, may have\n"
+);
+  printf("    a boundary marker.\n\n");
+  printf(
+"    If -q, -a, -u, and -s are not selected, Triangle produces a constrained\n"
+);
+  printf(
+"    Delaunay triangulation (CDT), in which each segment appears as a single\n"
+);
+  printf(
+"    edge in the triangulation.  If -q, -a, -u, or -s is selected, Triangle\n"
+);
+  printf(
+"    produces a conforming constrained Delaunay triangulation (CCDT), in\n");
+  printf(
+"    which segments may be subdivided into smaller edges.  If -L is selected\n"
+);
+  printf(
+"    as well, Triangle produces a conforming Delaunay triangulation, so\n");
+  printf(
+"    every triangle is Delaunay, and not just constrained Delaunay.\n");
+  printf("\n");
+  printf(
+"    The third section lists holes (and concavities, if -c is selected) in\n");
+  printf(
+"    the triangulation.  Holes are specified by identifying a point inside\n");
+  printf(
+"    each hole.  After the triangulation is formed, Triangle creates holes\n");
+  printf(
+"    by eating triangles, spreading out from each hole point until its\n");
+  printf(
+"    progress is blocked by PSLG segments; you must be careful to enclose\n");
+  printf(
+"    each hole in segments, or your whole triangulation might be eaten away.\n"
+);
+  printf(
+"    If the two triangles abutting a segment are eaten, the segment itself\n");
+  printf(
+"    is also eaten.  Do not place a hole directly on a segment; if you do,\n");
+  printf("    Triangle chooses one side of the segment arbitrarily.\n\n");
+  printf(
+"    The optional fourth section lists regional attributes (to be assigned\n");
+  printf(
+"    to all triangles in a region) and regional constraints on the maximum\n");
+  printf(
+"    triangle area.  Triangle reads this section only if the -A switch is\n");
+  printf(
+"    used or the -a switch is used without a number following it, and the -r\n"
+);
+  printf(
+"    switch is not used.  Regional attributes and area constraints are\n");
+  printf(
+"    propagated in the same manner as holes; you specify a point for each\n");
+  printf(
+"    attribute and/or constraint, and the attribute and/or constraint\n");
+  printf(
+"    affects the whole region (bounded by segments) containing the point.\n");
+  printf(
+"    If two values are written on a line after the x and y coordinate, the\n");
+  printf(
+"    first such value is assumed to be a regional attribute (but is only\n");
+  printf(
+"    applied if the -A switch is selected), and the second value is assumed\n"
+);
+  printf(
+"    to be a regional area constraint (but is only applied if the -a switch\n"
+);
+  printf(
+"    is selected).  You may specify just one value after the coordinates,\n");
+  printf(
+"    which can serve as both an attribute and an area constraint, depending\n"
+);
+  printf(
+"    on the choice of switches.  If you are using the -A and -a switches\n");
+  printf(
+"    simultaneously and wish to assign an attribute to some region without\n");
+  printf("    imposing an area constraint, use a negative maximum area.\n\n");
+  printf(
+"    When a triangulation is created from a .poly file, you must either\n");
+  printf(
+"    enclose the entire region to be triangulated in PSLG segments, or\n");
+  printf(
+"    use the -c switch, which encloses the convex hull of the input vertex\n");
+  printf(
+"    set.  If you do not use the -c switch, Triangle eats all triangles that\n"
+);
+  printf(
+"    are not enclosed by segments; if you are not careful, your whole\n");
+  printf(
+"    triangulation may be eaten away.  If you do use the -c switch, you can\n"
+);
+  printf(
+"    still produce concavities by the appropriate placement of holes just\n");
+  printf("    within the convex hull.\n\n");
+  printf(
+"    An ideal PSLG has no intersecting segments, nor any vertices that lie\n");
+  printf(
+"    upon segments (except, of course, the endpoints of each segment.)  You\n"
+);
+  printf(
+"    aren't required to make your .poly files ideal, but you should be aware\n"
+);
+  printf(
+"    of what can go wrong.  Segment intersections are relatively safe--\n");
+  printf(
+"    Triangle calculates the intersection points for you and adds them to\n");
+  printf(
+"    the triangulation--as long as your machine's floating-point precision\n");
+  printf(
+"    doesn't become a problem.  You are tempting the fates if you have three\n"
+);
+  printf(
+"    segments that cross at the same location, and expect Triangle to figure\n"
+);
+  printf(
+"    out where the intersection point is.  Thanks to floating-point roundoff\n"
+);
+  printf(
+"    error, Triangle will probably decide that the three segments intersect\n"
+);
+  printf(
+"    at three different points, and you will find a minuscule triangle in\n");
+  printf(
+"    your output--unless Triangle tries to refine the tiny triangle, uses\n");
+  printf(
+"    up the last bit of machine precision, and fails to terminate at all.\n");
+  printf(
+"    You're better off putting the intersection point in the input files,\n");
+  printf(
+"    and manually breaking up each segment into two.  Similarly, if you\n");
+  printf(
+"    place a vertex at the middle of a segment, and hope that Triangle will\n"
+);
+  printf(
+"    break up the segment at that vertex, you might get lucky.  On the other\n"
+);
+  printf(
+"    hand, Triangle might decide that the vertex doesn't lie precisely on\n");
+  printf(
+"    the segment, and you'll have a needle-sharp triangle in your output--or\n"
+);
+  printf("    a lot of tiny triangles if you're generating a quality mesh.\n");
+  printf("\n");
+  printf(
+"    When Triangle reads a .poly file, it also writes a .poly file, which\n");
+  printf(
+"    includes all edges that are parts of input segments.  If the -c switch\n"
+);
+  printf(
+"    is used, the output .poly file also includes all of the edges on the\n");
+  printf(
+"    convex hull.  Hence, the output .poly file is useful for finding edges\n"
+);
+  printf(
+"    associated with input segments and for setting boundary conditions in\n");
+  printf(
+"    finite element simulations.  Moreover, you will need it if you plan to\n"
+);
+  printf(
+"    refine the output mesh, and don't want segments to be missing in later\n"
+);
+  printf("    triangulations.\n\n");
+  printf("  .area files:\n");
+  printf("    First line:  <# of triangles>\n");
+  printf("    Following lines:  <triangle #> <maximum area>\n\n");
+  printf(
+"    An .area file associates with each triangle a maximum area that is used\n"
+);
+  printf(
+"    for mesh refinement.  As with other file formats, every triangle must\n");
+  printf(
+"    be represented, and they must be numbered consecutively.  A triangle\n");
+  printf(
+"    may be left unconstrained by assigning it a negative maximum area.\n");
+  printf("\n");
+  printf("  .edge files:\n");
+  printf("    First line:  <# of edges> <# of boundary markers (0 or 1)>\n");
+  printf(
+"    Following lines:  <edge #> <endpoint> <endpoint> [boundary marker]\n");
+  printf("\n");
+  printf(
+"    Endpoints are indices into the corresponding .node file.  Triangle can\n"
+);
+  printf(
+"    produce .edge files (use the -e switch), but cannot read them.  The\n");
+  printf(
+"    optional column of boundary markers is suppressed by the -B switch.\n");
+  printf("\n");
+  printf(
+"    In Voronoi diagrams, one also finds a special kind of edge that is an\n");
+  printf(
+"    infinite ray with only one endpoint.  For these edges, a different\n");
+  printf("    format is used:\n\n");
+  printf("        <edge #> <endpoint> -1 <direction x> <direction y>\n\n");
+  printf(
+"    The `direction' is a floating-point vector that indicates the direction\n"
+);
+  printf("    of the infinite ray.\n\n");
+  printf("  .neigh files:\n");
+  printf(
+"    First line:  <# of triangles> <# of neighbors per triangle (always 3)>\n"
+);
+  printf(
+"    Following lines:  <triangle #> <neighbor> <neighbor> <neighbor>\n");
+  printf("\n");
+  printf(
+"    Neighbors are indices into the corresponding .ele file.  An index of -1\n"
+);
+  printf(
+"    indicates no neighbor (because the triangle is on an exterior\n");
+  printf(
+"    boundary).  The first neighbor of triangle i is opposite the first\n");
+  printf("    corner of triangle i, and so on.\n\n");
+  printf(
+"    Triangle can produce .neigh files (use the -n switch), but cannot read\n"
+);
+  printf("    them.\n\n");
+  printf("Boundary Markers:\n\n");
+  printf(
+"  Boundary markers are tags used mainly to identify which output vertices\n");
+  printf(
+"  and edges are associated with which PSLG segment, and to identify which\n");
+  printf(
+"  vertices and edges occur on a boundary of the triangulation.  A common\n");
+  printf(
+"  use is to determine where boundary conditions should be applied to a\n");
+  printf(
+"  finite element mesh.  You can prevent boundary markers from being written\n"
+);
+  printf("  into files produced by Triangle by using the -B switch.\n\n");
+  printf(
+"  The boundary marker associated with each segment in an output .poly file\n"
+);
+  printf("  and each edge in an output .edge file is chosen as follows:\n");
+  printf(
+"    - If an output edge is part or all of a PSLG segment with a nonzero\n");
+  printf(
+"      boundary marker, then the edge is assigned the same marker.\n");
+  printf(
+"    - Otherwise, if the edge occurs on a boundary of the triangulation\n");
+  printf(
+"      (including boundaries of holes), then the edge is assigned the marker\n"
+);
+  printf("      one (1).\n");
+  printf("    - Otherwise, the edge is assigned the marker zero (0).\n");
+  printf(
+"  The boundary marker associated with each vertex in an output .node file\n");
+  printf("  is chosen as follows:\n");
+  printf(
+"    - If a vertex is assigned a nonzero boundary marker in the input file,\n"
+);
+  printf(
+"      then it is assigned the same marker in the output .node file.\n");
+  printf(
+"    - Otherwise, if the vertex lies on a PSLG segment (including the\n");
+  printf(
+"      segment's endpoints) with a nonzero boundary marker, then the vertex\n"
+);
+  printf(
+"      is assigned the same marker.  If the vertex lies on several such\n");
+  printf("      segments, one of the markers is chosen arbitrarily.\n");
+  printf(
+"    - Otherwise, if the vertex occurs on a boundary of the triangulation,\n");
+  printf("      then the vertex is assigned the marker one (1).\n");
+  printf("    - Otherwise, the vertex is assigned the marker zero (0).\n");
+  printf("\n");
+  printf(
+"  If you want Triangle to determine for you which vertices and edges are on\n"
+);
+  printf(
+"  the boundary, assign them the boundary marker zero (or use no markers at\n"
+);
+  printf(
+"  all) in your input files.  In the output files, all boundary vertices,\n");
+  printf("  edges, and segments are assigned the value one.\n\n");
+  printf("Triangulation Iteration Numbers:\n\n");
+  printf(
+"  Because Triangle can read and refine its own triangulations, input\n");
+  printf(
+"  and output files have iteration numbers.  For instance, Triangle might\n");
+  printf(
+"  read the files mesh.3.node, mesh.3.ele, and mesh.3.poly, refine the\n");
+  printf(
+"  triangulation, and output the files mesh.4.node, mesh.4.ele, and\n");
+  printf("  mesh.4.poly.  Files with no iteration number are treated as if\n");
+  printf(
+"  their iteration number is zero; hence, Triangle might read the file\n");
+  printf(
+"  points.node, triangulate it, and produce the files points.1.node and\n");
+  printf("  points.1.ele.\n\n");
+  printf(
+"  Iteration numbers allow you to create a sequence of successively finer\n");
+  printf(
+"  meshes suitable for multigrid methods.  They also allow you to produce a\n"
+);
+  printf(
+"  sequence of meshes using error estimate-driven mesh refinement.\n");
+  printf("\n");
+  printf(
+"  If you're not using refinement or quality meshing, and you don't like\n");
+  printf(
+"  iteration numbers, use the -I switch to disable them.  This switch also\n");
+  printf(
+"  disables output of .node and .poly files to prevent your input files from\n"
+);
+  printf(
+"  being overwritten.  (If the input is a .poly file that contains its own\n");
+  printf("  points, a .node file is written.)\n\n");
+  printf("Examples of How to Use Triangle:\n\n");
+  printf(
+"  `triangle dots' reads vertices from dots.node, and writes their Delaunay\n"
+);
+  printf(
+"  triangulation to dots.1.node and dots.1.ele.  (dots.1.node is identical\n");
+  printf(
+"  to dots.node.)  `triangle -I dots' writes the triangulation to dots.ele\n");
+  printf(
+"  instead.  (No additional .node file is needed, so none is written.)\n");
+  printf("\n");
+  printf(
+"  `triangle -pe object.1' reads a PSLG from object.1.poly (and possibly\n");
+  printf(
+"  object.1.node, if the vertices are omitted from object.1.poly) and writes\n"
+);
+  printf(
+"  its constrained Delaunay triangulation to object.2.node and object.2.ele.\n"
+);
+  printf(
+"  The segments are copied to object.2.poly, and all edges are written to\n");
+  printf("  object.2.edge.\n\n");
+  printf(
+"  `triangle -pq31.5a.1 object' reads a PSLG from object.poly (and possibly\n"
+);
+  printf(
+"  object.node), generates a mesh whose angles are all 31.5 degrees or\n");
+  printf(
+"  greater and whose triangles all have areas of 0.1 or less, and writes the\n"
+);
+  printf(
+"  mesh to object.1.node and object.1.ele.  Each segment may be broken up\n");
+  printf("  into multiple subsegments; these are written to object.1.poly.\n");
+  printf("\n");
+  printf(
+"  Here is a sample file `box.poly' describing a square with a square hole:\n"
+);
+  printf("\n");
+  printf(
+"    # A box with eight vertices in 2D, no attributes, one boundary marker.\n"
+);
+  printf("    8 2 0 1\n");
+  printf("     # Outer box has these vertices:\n");
+  printf("     1   0 0   0\n");
+  printf("     2   0 3   0\n");
+  printf("     3   3 0   0\n");
+  printf("     4   3 3   33     # A special marker for this vertex.\n");
+  printf("     # Inner square has these vertices:\n");
+  printf("     5   1 1   0\n");
+  printf("     6   1 2   0\n");
+  printf("     7   2 1   0\n");
+  printf("     8   2 2   0\n");
+  printf("    # Five segments with boundary markers.\n");
+  printf("    5 1\n");
+  printf("     1   1 2   5      # Left side of outer box.\n");
+  printf("     # Square hole has these segments:\n");
+  printf("     2   5 7   0\n");
+  printf("     3   7 8   0\n");
+  printf("     4   8 6   10\n");
+  printf("     5   6 5   0\n");
+  printf("    # One hole in the middle of the inner square.\n");
+  printf("    1\n");
+  printf("     1   1.5 1.5\n");
+  printf("\n");
+  printf(
+"  Note that some segments are missing from the outer square, so one must\n");
+  printf(
+"  use the `-c' switch.  After `triangle -pqc box.poly', here is the output\n"
+);
+  printf(
+"  file `box.1.node', with twelve vertices.  The last four vertices were\n");
+  printf(
+"  added to meet the angle constraint.  Vertices 1, 2, and 9 have markers\n");
+  printf(
+"  from segment 1.  Vertices 6 and 8 have markers from segment 4.  All the\n");
+  printf(
+"  other vertices but 4 have been marked to indicate that they lie on a\n");
+  printf("  boundary.\n\n");
+  printf("    12  2  0  1\n");
+  printf("       1    0   0      5\n");
+  printf("       2    0   3      5\n");
+  printf("       3    3   0      1\n");
+  printf("       4    3   3     33\n");
+  printf("       5    1   1      1\n");
+  printf("       6    1   2     10\n");
+  printf("       7    2   1      1\n");
+  printf("       8    2   2     10\n");
+  printf("       9    0   1.5    5\n");
+  printf("      10    1.5   0    1\n");
+  printf("      11    3   1.5    1\n");
+  printf("      12    1.5   3    1\n");
+  printf("    # Generated by triangle -pqc box.poly\n");
+  printf("\n");
+  printf("  Here is the output file `box.1.ele', with twelve triangles.\n");
+  printf("\n");
+  printf("    12  3  0\n");
+  printf("       1     5   6   9\n");
+  printf("       2    10   3   7\n");
+  printf("       3     6   8  12\n");
+  printf("       4     9   1   5\n");
+  printf("       5     6   2   9\n");
+  printf("       6     7   3  11\n");
+  printf("       7    11   4   8\n");
+  printf("       8     7   5  10\n");
+  printf("       9    12   2   6\n");
+  printf("      10     8   7  11\n");
+  printf("      11     5   1  10\n");
+  printf("      12     8   4  12\n");
+  printf("    # Generated by triangle -pqc box.poly\n\n");
+  printf(
+"  Here is the output file `box.1.poly'.  Note that segments have been added\n"
+);
+  printf(
+"  to represent the convex hull, and some segments have been split by newly\n"
+);
+  printf(
+"  added vertices.  Note also that <# of vertices> is set to zero to\n");
+  printf("  indicate that the vertices should be read from the .node file.\n");
+  printf("\n");
+  printf("    0  2  0  1\n");
+  printf("    12  1\n");
+  printf("       1     1   9     5\n");
+  printf("       2     5   7     1\n");
+  printf("       3     8   7     1\n");
+  printf("       4     6   8    10\n");
+  printf("       5     5   6     1\n");
+  printf("       6     3  10     1\n");
+  printf("       7     4  11     1\n");
+  printf("       8     2  12     1\n");
+  printf("       9     9   2     5\n");
+  printf("      10    10   1     1\n");
+  printf("      11    11   3     1\n");
+  printf("      12    12   4     1\n");
+  printf("    1\n");
+  printf("       1   1.5 1.5\n");
+  printf("    # Generated by triangle -pqc box.poly\n");
+  printf("\n");
+  printf("Refinement and Area Constraints:\n");
+  printf("\n");
+  printf(
+"  The -r switch causes a mesh (.node and .ele files) to be read and\n");
+  printf(
+"  refined.  If the -p switch is also used, a .poly file is read and used to\n"
+);
+  printf(
+"  specify edges that are constrained and cannot be eliminated (although\n");
+  printf(
+"  they can be divided into smaller edges) by the refinement process.\n");
+  printf("\n");
+  printf(
+"  When you refine a mesh, you generally want to impose tighter quality\n");
+  printf(
+"  constraints.  One way to accomplish this is to use -q with a larger\n");
+  printf(
+"  angle, or -a followed by a smaller area than you used to generate the\n");
+  printf(
+"  mesh you are refining.  Another way to do this is to create an .area\n");
+  printf(
+"  file, which specifies a maximum area for each triangle, and use the -a\n");
+  printf(
+"  switch (without a number following).  Each triangle's area constraint is\n"
+);
+  printf(
+"  applied to that triangle.  Area constraints tend to diffuse as the mesh\n");
+  printf(
+"  is refined, so if there are large variations in area constraint between\n");
+  printf("  adjacent triangles, you may not get the results you want.\n\n");
+  printf(
+"  If you are refining a mesh composed of linear (three-node) elements, the\n"
+);
+  printf(
+"  output mesh contains all the nodes present in the input mesh, in the same\n"
+);
+  printf(
+"  order, with new nodes added at the end of the .node file.  However, the\n");
+  printf(
+"  refinement is not hierarchical: there is no guarantee that each output\n");
+  printf(
+"  element is contained in a single input element.  Often, output elements\n");
+  printf(
+"  overlap two input elements, and some input edges are not present in the\n");
+  printf(
+"  output mesh.  Hence, a sequence of refined meshes forms a hierarchy of\n");
+  printf(
+"  nodes, but not a hierarchy of elements.  If you refine a mesh of higher-\n"
+);
+  printf(
+"  order elements, the hierarchical property applies only to the nodes at\n");
+  printf(
+"  the corners of an element; other nodes may not be present in the refined\n"
+);
+  printf("  mesh.\n\n");
+  printf(
+"  Maximum area constraints in .poly files operate differently from those in\n"
+);
+  printf(
+"  .area files.  A maximum area in a .poly file applies to the whole\n");
+  printf(
+"  (segment-bounded) region in which a point falls, whereas a maximum area\n");
+  printf(
+"  in an .area file applies to only one triangle.  Area constraints in .poly\n"
+);
+  printf(
+"  files are used only when a mesh is first generated, whereas area\n");
+  printf(
+"  constraints in .area files are used only to refine an existing mesh, and\n"
+);
+  printf(
+"  are typically based on a posteriori error estimates resulting from a\n");
+  printf("  finite element simulation on that mesh.\n\n");
+  printf(
+"  `triangle -rq25 object.1' reads object.1.node and object.1.ele, then\n");
+  printf(
+"  refines the triangulation to enforce a 25 degree minimum angle, and then\n"
+);
+  printf(
+"  writes the refined triangulation to object.2.node and object.2.ele.\n");
+  printf("\n");
+  printf(
+"  `triangle -rpaa6.2 z.3' reads z.3.node, z.3.ele, z.3.poly, and z.3.area.\n"
+);
+  printf(
+"  After reconstructing the mesh and its subsegments, Triangle refines the\n");
+  printf(
+"  mesh so that no triangle has area greater than 6.2, and furthermore the\n");
+  printf(
+"  triangles satisfy the maximum area constraints in z.3.area.  No angle\n");
+  printf(
+"  bound is imposed at all.  The output is written to z.4.node, z.4.ele, and\n"
+);
+  printf("  z.4.poly.\n\n");
+  printf(
+"  The sequence `triangle -qa1 x', `triangle -rqa.3 x.1', `triangle -rqa.1\n");
+  printf(
+"  x.2' creates a sequence of successively finer meshes x.1, x.2, and x.3,\n");
+  printf("  suitable for multigrid.\n\n");
+  printf("Convex Hulls and Mesh Boundaries:\n\n");
+  printf(
+"  If the input is a vertex set (rather than a PSLG), Triangle produces its\n"
+);
+  printf(
+"  convex hull as a by-product in the output .poly file if you use the -c\n");
+  printf(
+"  switch.  There are faster algorithms for finding a two-dimensional convex\n"
+);
+  printf(
+"  hull than triangulation, of course, but this one comes for free.\n");
+  printf("\n");
+  printf(
+"  If the input is an unconstrained mesh (you are using the -r switch but\n");
+  printf(
+"  not the -p switch), Triangle produces a list of its boundary edges\n");
+  printf(
+"  (including hole boundaries) as a by-product when you use the -c switch.\n");
+  printf(
+"  If you also use the -p switch, the output .poly file contains all the\n");
+  printf("  segments from the input .poly file as well.\n\n");
+  printf("Voronoi Diagrams:\n\n");
+  printf(
+"  The -v switch produces a Voronoi diagram, in files suffixed .v.node and\n");
+  printf(
+"  .v.edge.  For example, `triangle -v points' reads points.node, produces\n");
+  printf(
+"  its Delaunay triangulation in points.1.node and points.1.ele, and\n");
+  printf(
+"  produces its Voronoi diagram in points.1.v.node and points.1.v.edge.  The\n"
+);
+  printf(
+"  .v.node file contains a list of all Voronoi vertices, and the .v.edge\n");
+  printf(
+"  file contains a list of all Voronoi edges, some of which may be infinite\n"
+);
+  printf(
+"  rays.  (The choice of filenames makes it easy to run the set of Voronoi\n");
+  printf("  vertices through Triangle, if so desired.)\n\n");
+  printf(
+"  This implementation does not use exact arithmetic to compute the Voronoi\n"
+);
+  printf(
+"  vertices, and does not check whether neighboring vertices are identical.\n"
+);
+  printf(
+"  Be forewarned that if the Delaunay triangulation is degenerate or\n");
+  printf(
+"  near-degenerate, the Voronoi diagram may have duplicate vertices,\n");
+  printf(
+"  crossing edges, or infinite rays whose direction vector is zero.\n");
+  printf("\n");
+  printf(
+"  The result is a valid Voronoi diagram only if Triangle's output is a true\n"
+);
+  printf(
+"  Delaunay triangulation.  The Voronoi output is usually meaningless (and\n");
+  printf(
+"  may contain crossing edges and other pathology) if the output is a CDT or\n"
+);
+  printf(
+"  CCDT, or if it has holes or concavities.  If the triangulation is convex\n"
+);
+  printf(
+"  and has no holes, this can be fixed by using the -L switch to ensure a\n");
+  printf("  conforming Delaunay triangulation is constructed.\n\n");
+  printf("Mesh Topology:\n\n");
+  printf(
+"  You may wish to know which triangles are adjacent to a certain Delaunay\n");
+  printf(
+"  edge in an .edge file, which Voronoi regions are adjacent to a certain\n");
+  printf(
+"  Voronoi edge in a .v.edge file, or which Voronoi regions are adjacent to\n"
+);
+  printf(
+"  each other.  All of this information can be found by cross-referencing\n");
+  printf(
+"  output files with the recollection that the Delaunay triangulation and\n");
+  printf("  the Voronoi diagram are planar duals.\n\n");
+  printf(
+"  Specifically, edge i of an .edge file is the dual of Voronoi edge i of\n");
+  printf(
+"  the corresponding .v.edge file, and is rotated 90 degrees counterclock-\n");
+  printf(
+"  wise from the Voronoi edge.  Triangle j of an .ele file is the dual of\n");
+  printf(
+"  vertex j of the corresponding .v.node file.  Voronoi region k is the dual\n"
+);
+  printf("  of vertex k of the corresponding .node file.\n\n");
+  printf(
+"  Hence, to find the triangles adjacent to a Delaunay edge, look at the\n");
+  printf(
+"  vertices of the corresponding Voronoi edge.  If the endpoints of a\n");
+  printf(
+"  Voronoi edge are Voronoi vertices 2 and 6 respectively, then triangles 2\n"
+);
+  printf(
+"  and 6 adjoin the left and right sides of the corresponding Delaunay edge,\n"
+);
+  printf(
+"  respectively.  To find the Voronoi regions adjacent to a Voronoi edge,\n");
+  printf(
+"  look at the endpoints of the corresponding Delaunay edge.  If the\n");
+  printf(
+"  endpoints of a Delaunay edge are input vertices 7 and 12, then Voronoi\n");
+  printf(
+"  regions 7 and 12 adjoin the right and left sides of the corresponding\n");
+  printf(
+"  Voronoi edge, respectively.  To find which Voronoi regions are adjacent\n");
+  printf("  to each other, just read the list of Delaunay edges.\n\n");
+  printf(
+"  Triangle does not write a list of Voronoi regions, but one can be\n");
+  printf(
+"  reconstructed straightforwardly.  For instance, to find all the edges of\n"
+);
+  printf(
+"  Voronoi region 1, search the output .edge file for every edge that has\n");
+  printf(
+"  input vertex 1 as an endpoint.  The corresponding dual edges in the\n");
+  printf("  output .v.edge file form the boundary of Voronoi region 1.\n\n");
+  printf("Quadratic Elements:\n\n");
+  printf(
+"  Triangle generates meshes with subparametric quadratic elements if the\n");
+  printf(
+"  -o2 switch is specified.  Quadratic elements have six nodes per element,\n"
+);
+  printf(
+"  rather than three.  `Subparametric' means that the edges of the triangles\n"
+);
+  printf(
+"  are always straight, so that subparametric quadratic elements are\n");
+  printf(
+"  geometrically identical to linear elements, even though they can be used\n"
+);
+  printf(
+"  with quadratic interpolating functions.  The three extra nodes of an\n");
+  printf(
+"  element fall at the midpoints of the three edges, with the fourth, fifth,\n"
+);
+  printf(
+"  and sixth nodes appearing opposite the first, second, and third corners\n");
+  printf("  respectively.\n\n");
+  printf("Statistics:\n\n");
+  printf(
+"  After generating a mesh, Triangle prints a count of the number of\n");
+  printf(
+"  vertices, triangles, edges, exterior boundary edges (including hole\n");
+  printf(
+"  boundaries), interior boundary edges, and segments in the output mesh.\n");
+  printf(
+"  If you've forgotten the statistics for an existing mesh, run Triangle on\n"
+);
+  printf(
+"  that mesh with the -rNEP switches to read the mesh and print the\n");
+  printf(
+"  statistics without writing any files.  Use -rpNEP if you've got a .poly\n");
+  printf("  file for the mesh.\n\n");
+  printf(
+"  The -V switch produces extended statistics, including a rough estimate\n");
+  printf(
+"  of memory use, the number of calls to geometric predicates, and\n");
+  printf("  histograms of triangle aspect ratios and angles in the mesh.\n\n");
+  printf("Exact Arithmetic:\n\n");
+  printf(
+"  Triangle uses adaptive exact arithmetic to perform what computational\n");
+  printf(
+"  geometers call the `orientation' and `incircle' tests.  If the floating-\n"
+);
+  printf(
+"  point arithmetic of your machine conforms to the IEEE 754 standard (as\n");
+  printf(
+"  most workstations do), and does not use extended precision internal\n");
+  printf(
+"  floating-point registers, then your output is guaranteed to be an\n");
+  printf(
+"  absolutely true Delaunay or constrained Delaunay triangulation, roundoff\n"
+);
+  printf(
+"  error notwithstanding.  The word `adaptive' implies that these arithmetic\n"
+);
+  printf(
+"  routines compute the result only to the precision necessary to guarantee\n"
+);
+  printf(
+"  correctness, so they are usually nearly as fast as their approximate\n");
+  printf("  counterparts.\n\n");
+  printf(
+"  Pentiums have extended precision floating-point registers.  These must be\n"
+);
+  printf(
+"  reconfigured so their precision is reduced to memory precision.  Triangle\n"
+);
+  printf(
+"  does this if it is compiled correctly.  See the makefile for details.\n");
+  printf("\n");
+  printf(
+"  The exact tests can be disabled with the -X switch.  On most inputs, this\n"
+);
+  printf(
+"  switch reduces the computation time by about eight percent--it's not\n");
+  printf(
+"  worth the risk.  There are rare difficult inputs (having many collinear\n");
+  printf(
+"  and cocircular vertices), however, for which the difference in speed\n");
+  printf(
+"  could be a factor of two.  Be forewarned that these are precisely the\n");
+  printf(
+"  inputs most likely to cause errors if you use the -X switch.  Hence, the\n"
+);
+  printf("  -X switch is not recommended.\n\n");
+  printf(
+"  Unfortunately, the exact tests don't solve every numerical problem.\n");
+  printf(
+"  Exact arithmetic is not used to compute the positions of new vertices,\n");
+  printf(
+"  because the bit complexity of vertex coordinates would grow without\n");
+  printf(
+"  bound.  Hence, segment intersections aren't computed exactly; in very\n");
+  printf(
+"  unusual cases, roundoff error in computing an intersection point might\n");
+  printf(
+"  actually lead to an inverted triangle and an invalid triangulation.\n");
+  printf(
+"  (This is one reason to compute your own intersection points in your .poly\n"
+);
+  printf(
+"  files.)  Similarly, exact arithmetic is not used to compute the vertices\n"
+);
+  printf("  of the Voronoi diagram.\n\n");
+  printf(
+"  Another pair of problems not solved by the exact arithmetic routines is\n");
+  printf(
+"  underflow and overflow.  If Triangle is compiled for double precision\n");
+  printf(
+"  arithmetic, I believe that Triangle's geometric predicates work correctly\n"
+);
+  printf(
+"  if the exponent of every input coordinate falls in the range [-148, 201].\n"
+);
+  printf(
+"  Underflow can silently prevent the orientation and incircle tests from\n");
+  printf(
+"  being performed exactly, while overflow typically causes a floating\n");
+  printf("  exception.\n\n");
+  printf("Calling Triangle from Another Program:\n\n");
+  printf("  Read the file triangle.h for details.\n\n");
+  printf("Troubleshooting:\n\n");
+  printf("  Please read this section before mailing me bugs.\n\n");
+  printf("  `My output mesh has no triangles!'\n\n");
+  printf(
+"    If you're using a PSLG, you've probably failed to specify a proper set\n"
+);
+  printf(
+"    of bounding segments, or forgotten to use the -c switch.  Or you may\n");
+  printf(
+"    have placed a hole badly, thereby eating all your triangles.  To test\n");
+  printf("    these possibilities, try again with the -c and -O switches.\n");
+  printf(
+"    Alternatively, all your input vertices may be collinear, in which case\n"
+);
+  printf("    you can hardly expect to triangulate them.\n\n");
+  printf("  `Triangle doesn't terminate, or just crashes.'\n\n");
+  printf(
+"    Bad things can happen when triangles get so small that the distance\n");
+  printf(
+"    between their vertices isn't much larger than the precision of your\n");
+  printf(
+"    machine's arithmetic.  If you've compiled Triangle for single-precision\n"
+);
+  printf(
+"    arithmetic, you might do better by recompiling it for double-precision.\n"
+);
+  printf(
+"    Then again, you might just have to settle for more lenient constraints\n"
+);
+  printf(
+"    on the minimum angle and the maximum area than you had planned.\n");
+  printf("\n");
+  printf(
+"    You can minimize precision problems by ensuring that the origin lies\n");
+  printf(
+"    inside your vertex set, or even inside the densest part of your\n");
+  printf(
+"    mesh.  If you're triangulating an object whose x coordinates all fall\n");
+  printf(
+"    between 6247133 and 6247134, you're not leaving much floating-point\n");
+  printf("    precision for Triangle to work with.\n\n");
+  printf(
+"    Precision problems can occur covertly if the input PSLG contains two\n");
+  printf(
+"    segments that meet (or intersect) at an extremely small angle, or if\n");
+  printf(
+"    such an angle is introduced by the -c switch.  If you don't realize\n");
+  printf(
+"    that a tiny angle is being formed, you might never discover why\n");
+  printf(
+"    Triangle is crashing.  To check for this possibility, use the -S switch\n"
+);
+  printf(
+"    (with an appropriate limit on the number of Steiner points, found by\n");
+  printf(
+"    trial-and-error) to stop Triangle early, and view the output .poly file\n"
+);
+  printf(
+"    with Show Me (described below).  Look carefully for regions where dense\n"
+);
+  printf(
+"    clusters of vertices are forming and for small angles between segments.\n"
+);
+  printf(
+"    Zoom in closely, as such segments might look like a single segment from\n"
+);
+  printf("    a distance.\n\n");
+  printf(
+"    If some of the input values are too large, Triangle may suffer a\n");
+  printf(
+"    floating exception due to overflow when attempting to perform an\n");
+  printf(
+"    orientation or incircle test.  (Read the section on exact arithmetic\n");
+  printf(
+"    above.)  Again, I recommend compiling Triangle for double (rather\n");
+  printf("    than single) precision arithmetic.\n\n");
+  printf(
+"    Unexpected problems can arise if you use quality meshing (-q, -a, or\n");
+  printf(
+"    -u) with an input that is not segment-bounded--that is, if your input\n");
+  printf(
+"    is a vertex set, or you're using the -c switch.  If the convex hull of\n"
+);
+  printf(
+"    your input vertices has collinear vertices on its boundary, an input\n");
+  printf(
+"    vertex that you think lies on the convex hull might actually lie just\n");
+  printf(
+"    inside the convex hull.  If so, an extremely thin triangle is formed by\n"
+);
+  printf(
+"    the vertex and the convex hull edge beside it.  When Triangle tries to\n"
+);
+  printf(
+"    refine the mesh to enforce angle and area constraints, extremely tiny\n");
+  printf(
+"    triangles may be formed, or Triangle may fail because of insufficient\n");
+  printf("    floating-point precision.\n\n");
+  printf(
+"  `The numbering of the output vertices doesn't match the input vertices.'\n"
+);
+  printf("\n");
+  printf(
+"    You may have had duplicate input vertices, or you may have eaten some\n");
+  printf(
+"    of your input vertices with a hole, or by placing them outside the area\n"
+);
+  printf(
+"    enclosed by segments.  In any case, you can solve the problem by not\n");
+  printf("    using the -j switch.\n\n");
+  printf(
+"  `Triangle executes without incident, but when I look at the resulting\n");
+  printf(
+"  mesh, it has overlapping triangles or other geometric inconsistencies.'\n");
+  printf("\n");
+  printf(
+"    If you select the -X switch, Triangle occasionally makes mistakes due\n");
+  printf(
+"    to floating-point roundoff error.  Although these errors are rare,\n");
+  printf(
+"    don't use the -X switch.  If you still have problems, please report the\n"
+);
+  printf("    bug.\n\n");
+  printf(
+"  Strange things can happen if you've taken liberties with your PSLG.  Do\n");
+  printf(
+"  you have a vertex lying in the middle of a segment?  Triangle sometimes\n");
+  printf(
+"  copes poorly with that sort of thing.  Do you want to lay out a collinear\n"
+);
+  printf(
+"  row of evenly spaced, segment-connected vertices?  Have you simply\n");
+  printf(
+"  defined one long segment connecting the leftmost vertex to the rightmost\n"
+);
+  printf(
+"  vertex, and a bunch of vertices lying along it?  This method occasionally\n"
+);
+  printf(
+"  works, especially with horizontal and vertical lines, but often it\n");
+  printf(
+"  doesn't, and you'll have to connect each adjacent pair of vertices with a\n"
+);
+  printf("  separate segment.  If you don't like it, tough.\n\n");
+  printf(
+"  Furthermore, if you have segments that intersect other than at their\n");
+  printf(
+"  endpoints, try not to let the intersections fall extremely close to PSLG\n"
+);
+  printf("  vertices or each other.\n\n");
+  printf(
+"  If you have problems refining a triangulation not produced by Triangle:\n");
+  printf(
+"  Are you sure the triangulation is geometrically valid?  Is it formatted\n");
+  printf(
+"  correctly for Triangle?  Are the triangles all listed so the first three\n"
+);
+  printf(
+"  vertices are their corners in counterclockwise order?  Are all of the\n");
+  printf(
+"  triangles constrained Delaunay?  Triangle's Delaunay refinement algorithm\n"
+);
+  printf("  assumes that it starts with a CDT.\n\n");
+  printf("Show Me:\n\n");
+  printf(
+"  Triangle comes with a separate program named `Show Me', whose primary\n");
+  printf(
+"  purpose is to draw meshes on your screen or in PostScript.  Its secondary\n"
+);
+  printf(
+"  purpose is to check the validity of your input files, and do so more\n");
+  printf(
+"  thoroughly than Triangle does.  Unlike Triangle, Show Me requires that\n");
+  printf("  you have the X Windows system.\n\n");
+  printf("Triangle on the Web:\n\n");
+  printf(
+"  To see an illustrated, updated version of these instructions, check out\n");
+  printf("\n");
+  printf("    http://www.cs.cmu.edu/~quake/triangle.html\n");
+  printf("\n");
+  printf("A Brief Plea:\n");
+  printf("\n");
+  printf(
+"  If you use Triangle, and especially if you use it to accomplish real\n");
+  printf(
+"  work, I would like very much to hear from you.  A short letter or email\n");
+  printf(
+"  (to jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot\n"
+);
+  printf(
+"  to me.  The more people I know are using this program, the more easily I\n"
+);
+  printf(
+"  can justify spending time on improvements, which in turn will benefit\n");
+  printf(
+"  you.  Also, I can put you on a list to receive email whenever a new\n");
+  printf("  version of Triangle is available.\n\n");
+  printf(
+"  If you use a mesh generated by Triangle in a publication, please include\n"
+);
+  printf("  an acknowledgment as well.\n\n");
+  printf("Research credit:\n\n");
+  printf(
+"  Of course, I can take credit for only a fraction of the ideas that made\n");
+  printf(
+"  this mesh generator possible.  Triangle owes its existence to the efforts\n"
+);
+  printf(
+"  of many fine computational geometers and other researchers, including\n");
+  printf(
+"  Marshall Bern, L. Paul Chew, Boris Delaunay, Rex A. Dwyer, David\n");
+  printf(
+"  Eppstein, Steven Fortune, Leonidas J. Guibas, Donald E. Knuth, Charles L.\n"
+);
+  printf(
+"  Lawson, Der-Tsai Lee, Ernst P. Mucke, Douglas M. Priest, Jim Ruppert,\n");
+  printf(
+"  Isaac Saias, Bruce J. Schachter, Micha Sharir, Daniel D. Sleator, Jorge\n");
+  printf(
+"  Stolfi, Robert E. Tarjan, Alper Ungor, Christopher J. Van Wyk, and Binhai\n"
+);
+  printf("  Zhu.  See the comments at the beginning of the source code for\n");
+  printf("  references.\n\n");
+  exit(0);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  internalerror()   Ask the user to send me the defective product.  Exit.  */
+/*                                                                           */
+/*****************************************************************************/
+
+void internalerror()
+{
+  printf("  Please report this bug to jrs@cs.berkeley.edu\n");
+  printf("  Include the message above, your input data set, and the exact\n");
+  printf("    command line you used to run Triangle.\n");
+  exit(1);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  parsecommandline()   Read the command line, identify switches, and set   */
+/*                       up options and file names.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void parsecommandline(int argc, char **argv, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void parsecommandline(argc, argv, b)
+int argc;
+char **argv;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+#ifdef TRILIBRARY
+#define STARTINDEX 0
+#else /* not TRILIBRARY */
+#define STARTINDEX 1
+  int increment;
+  int meshnumber;
+#endif /* not TRILIBRARY */
+  int i, j, k;
+  char workstring[FILENAMESIZE];
+
+  b->poly = b->refine = b->quality = 0;
+  b->vararea = b->fixedarea = b->usertest = 0;
+  b->regionattrib = b->convex = b->weighted = b->jettison = 0;
+  b->firstnumber = 1;
+  b->edgesout = b->voronoi = b->neighbors = b->geomview = 0;
+  b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0;
+  b->noiterationnum = 0;
+  b->noholes = b->noexact = 0;
+  b->incremental = b->sweepline = 0;
+  b->dwyer = 1;
+  b->splitseg = 0;
+  b->docheck = 0;
+  b->nobisect = 0;
+  b->nolenses = 0;
+  b->steiner = -1;
+  b->order = 1;
+  b->minangle = 0.0;
+  b->maxarea = -1.0;
+  b->quiet = b->verbose = 0;
+#ifndef TRILIBRARY
+  b->innodefilename[0] = '\0';
+#endif /* not TRILIBRARY */
+
+  for (i = STARTINDEX; i < argc; i++) {
+#ifndef TRILIBRARY
+    if (argv[i][0] == '-') {
+#endif /* not TRILIBRARY */
+      for (j = STARTINDEX; argv[i][j] != '\0'; j++) {
+        if (argv[i][j] == 'p') {
+          b->poly = 1;
+	}
+#ifndef CDT_ONLY
+        if (argv[i][j] == 'r') {
+          b->refine = 1;
+	}
+        if (argv[i][j] == 'q') {
+          b->quality = 1;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            b->minangle = (REAL) strtod(workstring, (char **) NULL);
+	  } else {
+            b->minangle = 20.0;
+	  }
+	}
+        if (argv[i][j] == 'a') {
+          b->quality = 1;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            b->fixedarea = 1;
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            b->maxarea = (REAL) strtod(workstring, (char **) NULL);
+            if (b->maxarea <= 0.0) {
+              printf("Error:  Maximum area must be greater than zero.\n");
+              exit(1);
+	    }
+	  } else {
+            b->vararea = 1;
+	  }
+	}
+        if (argv[i][j] == 'u') {
+          b->quality = 1;
+          b->usertest = 1;
+        }
+#endif /* not CDT_ONLY */
+        if (argv[i][j] == 'A') {
+          b->regionattrib = 1;
+        }
+        if (argv[i][j] == 'c') {
+          b->convex = 1;
+        }
+        if (argv[i][j] == 'w') {
+          b->weighted = 1;
+        }
+        if (argv[i][j] == 'W') {
+          b->weighted = 2;
+        }
+        if (argv[i][j] == 'j') {
+          b->jettison = 1;
+        }
+        if (argv[i][j] == 'z') {
+          b->firstnumber = 0;
+        }
+        if (argv[i][j] == 'e') {
+          b->edgesout = 1;
+	}
+        if (argv[i][j] == 'v') {
+          b->voronoi = 1;
+	}
+        if (argv[i][j] == 'n') {
+          b->neighbors = 1;
+	}
+        if (argv[i][j] == 'g') {
+          b->geomview = 1;
+	}
+        if (argv[i][j] == 'B') {
+          b->nobound = 1;
+	}
+        if (argv[i][j] == 'P') {
+          b->nopolywritten = 1;
+	}
+        if (argv[i][j] == 'N') {
+          b->nonodewritten = 1;
+	}
+        if (argv[i][j] == 'E') {
+          b->noelewritten = 1;
+	}
+#ifndef TRILIBRARY
+        if (argv[i][j] == 'I') {
+          b->noiterationnum = 1;
+	}
+#endif /* not TRILIBRARY */
+        if (argv[i][j] == 'O') {
+          b->noholes = 1;
+	}
+        if (argv[i][j] == 'X') {
+          b->noexact = 1;
+	}
+        if (argv[i][j] == 'o') {
+          if (argv[i][j + 1] == '2') {
+            j++;
+            b->order = 2;
+          }
+	}
+#ifndef CDT_ONLY
+        if (argv[i][j] == 'Y') {
+          b->nobisect++;
+	}
+        if (argv[i][j] == 'S') {
+          b->steiner = 0;
+          while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+            j++;
+            b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0');
+          }
+        }
+#endif /* not CDT_ONLY */
+#ifndef REDUCED
+        if (argv[i][j] == 'i') {
+          b->incremental = 1;
+        }
+        if (argv[i][j] == 'F') {
+          b->sweepline = 1;
+        }
+#endif /* not REDUCED */
+        if (argv[i][j] == 'l') {
+          b->dwyer = 0;
+        }
+#ifndef REDUCED
+#ifndef CDT_ONLY
+        if (argv[i][j] == 's') {
+          b->splitseg = 1;
+        }
+        if (argv[i][j] == 'L') {
+          b->nolenses = 1;
+        }
+#endif /* not CDT_ONLY */
+        if (argv[i][j] == 'C') {
+          b->docheck = 1;
+        }
+#endif /* not REDUCED */
+        if (argv[i][j] == 'Q') {
+          b->quiet = 1;
+        }
+        if (argv[i][j] == 'V') {
+          b->verbose++;
+        }
+#ifndef TRILIBRARY
+        if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
+            (argv[i][j] == '?')) {
+          info();
+	}
+#endif /* not TRILIBRARY */
+      }
+#ifndef TRILIBRARY
+    } else {
+      strncpy(b->innodefilename, argv[i], FILENAMESIZE - 1);
+      b->innodefilename[FILENAMESIZE - 1] = '\0';
+    }
+#endif /* not TRILIBRARY */
+  }
+#ifndef TRILIBRARY
+  if (b->innodefilename[0] == '\0') {
+    syntax();
+  }
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".node")) {
+    b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+  }
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".poly")) {
+    b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+    b->poly = 1;
+  }
+#ifndef CDT_ONLY
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 4], ".ele")) {
+    b->innodefilename[strlen(b->innodefilename) - 4] = '\0';
+    b->refine = 1;
+  }
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".area")) {
+    b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+    b->refine = 1;
+    b->quality = 1;
+    b->vararea = 1;
+  }
+#endif /* not CDT_ONLY */
+#endif /* not TRILIBRARY */
+  b->usesegments = b->poly || b->refine || b->quality || b->convex;
+  b->goodangle = cos(b->minangle * PI / 180.0);
+  if (b->goodangle == 1.0) {
+    b->offconstant = 0.0;
+  } else {
+    b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle));
+  }
+  b->goodangle *= b->goodangle;
+  if (b->refine && b->noiterationnum) {
+    printf(
+      "Error:  You cannot use the -I switch when refining a triangulation.\n");
+    exit(1);
+  }
+  /* Be careful not to allocate space for element area constraints that */
+  /*   will never be assigned any value (other than the default -1.0).  */
+  if (!b->refine && !b->poly) {
+    b->vararea = 0;
+  }
+  /* Be careful not to add an extra attribute to each element unless the */
+  /*   input supports it (PSLG in, but not refining a preexisting mesh). */
+  if (b->refine || !b->poly) {
+    b->regionattrib = 0;
+  }
+  /* Regular/weighted triangulations are incompatible with PSLGs */
+  /*   and meshing.                                              */
+  if (b->weighted && (b->poly || b->quality)) {
+    b->weighted = 0;
+    if (!b->quiet) {
+      printf("Warning:  weighted triangulations (-w, -W) are incompatible\n");
+      printf("  with PSLGs (-p) and meshing (-q, -a, -u).  Weights ignored.\n"
+             );
+    }
+  }
+  if (b->jettison && b->nonodewritten && !b->quiet) {
+    printf("Warning:  -j and -N switches are somewhat incompatible.\n");
+    printf("  If any vertices are jettisoned, you will need the output\n");
+    printf("  .node file to reconstruct the new node indices.");
+  }
+
+#ifndef TRILIBRARY
+  strcpy(b->inpolyfilename, b->innodefilename);
+  strcpy(b->inelefilename, b->innodefilename);
+  strcpy(b->areafilename, b->innodefilename);
+  increment = 0;
+  strcpy(workstring, b->innodefilename);
+  j = 1;
+  while (workstring[j] != '\0') {
+    if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
+      increment = j + 1;
+    }
+    j++;
+  }
+  meshnumber = 0;
+  if (increment > 0) {
+    j = increment;
+    do {
+      if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
+        meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
+      } else {
+        increment = 0;
+      }
+      j++;
+    } while (workstring[j] != '\0');
+  }
+  if (b->noiterationnum) {
+    strcpy(b->outnodefilename, b->innodefilename);
+    strcpy(b->outelefilename, b->innodefilename);
+    strcpy(b->edgefilename, b->innodefilename);
+    strcpy(b->vnodefilename, b->innodefilename);
+    strcpy(b->vedgefilename, b->innodefilename);
+    strcpy(b->neighborfilename, b->innodefilename);
+    strcpy(b->offfilename, b->innodefilename);
+    strcat(b->outnodefilename, ".node");
+    strcat(b->outelefilename, ".ele");
+    strcat(b->edgefilename, ".edge");
+    strcat(b->vnodefilename, ".v.node");
+    strcat(b->vedgefilename, ".v.edge");
+    strcat(b->neighborfilename, ".neigh");
+    strcat(b->offfilename, ".off");
+  } else if (increment == 0) {
+    strcpy(b->outnodefilename, b->innodefilename);
+    strcpy(b->outpolyfilename, b->innodefilename);
+    strcpy(b->outelefilename, b->innodefilename);
+    strcpy(b->edgefilename, b->innodefilename);
+    strcpy(b->vnodefilename, b->innodefilename);
+    strcpy(b->vedgefilename, b->innodefilename);
+    strcpy(b->neighborfilename, b->innodefilename);
+    strcpy(b->offfilename, b->innodefilename);
+    strcat(b->outnodefilename, ".1.node");
+    strcat(b->outpolyfilename, ".1.poly");
+    strcat(b->outelefilename, ".1.ele");
+    strcat(b->edgefilename, ".1.edge");
+    strcat(b->vnodefilename, ".1.v.node");
+    strcat(b->vedgefilename, ".1.v.edge");
+    strcat(b->neighborfilename, ".1.neigh");
+    strcat(b->offfilename, ".1.off");
+  } else {
+    workstring[increment] = '%';
+    workstring[increment + 1] = 'd';
+    workstring[increment + 2] = '\0';
+    sprintf(b->outnodefilename, workstring, meshnumber + 1);
+    strcpy(b->outpolyfilename, b->outnodefilename);
+    strcpy(b->outelefilename, b->outnodefilename);
+    strcpy(b->edgefilename, b->outnodefilename);
+    strcpy(b->vnodefilename, b->outnodefilename);
+    strcpy(b->vedgefilename, b->outnodefilename);
+    strcpy(b->neighborfilename, b->outnodefilename);
+    strcpy(b->offfilename, b->outnodefilename);
+    strcat(b->outnodefilename, ".node");
+    strcat(b->outpolyfilename, ".poly");
+    strcat(b->outelefilename, ".ele");
+    strcat(b->edgefilename, ".edge");
+    strcat(b->vnodefilename, ".v.node");
+    strcat(b->vedgefilename, ".v.edge");
+    strcat(b->neighborfilename, ".neigh");
+    strcat(b->offfilename, ".off");
+  }
+  strcat(b->innodefilename, ".node");
+  strcat(b->inpolyfilename, ".poly");
+  strcat(b->inelefilename, ".ele");
+  strcat(b->areafilename, ".area");
+#endif /* not TRILIBRARY */
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* User interaction routines begin here                      *********/
+
+/********* Debugging routines begin here                             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  printtriangle()   Print out the details of an oriented triangle.         */
+/*                                                                           */
+/*  I originally wrote this procedure to simplify debugging; it can be       */
+/*  called directly from the debugger, and presents information about an     */
+/*  oriented triangle in digestible form.  It's also used when the           */
+/*  highest level of verbosity (`-VVV') is specified.                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void printtriangle(struct mesh *m, struct behavior *b, struct otri *t)
+#else /* not ANSI_DECLARATORS */
+void printtriangle(m, b, t)
+struct mesh *m;
+struct behavior *b;
+struct otri *t;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri printtri;
+  struct osub printsh;
+  vertex printvertex;
+
+  printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri,
+         t->orient);
+  decode(t->tri[0], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [0] = Outer space\n");
+  } else {
+    printf("    [0] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+  decode(t->tri[1], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [1] = Outer space\n");
+  } else {
+    printf("    [1] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+  decode(t->tri[2], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [2] = Outer space\n");
+  } else {
+    printf("    [2] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+
+  org(*t, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3);
+  else
+    printf("    Origin[%d] = x%lx  (%.12g, %.12g)\n",
+           (t->orient + 1) % 3 + 3, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+  dest(*t, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Dest  [%d] = NULL\n", (t->orient + 2) % 3 + 3);
+  else
+    printf("    Dest  [%d] = x%lx  (%.12g, %.12g)\n",
+           (t->orient + 2) % 3 + 3, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+  apex(*t, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Apex  [%d] = NULL\n", t->orient + 3);
+  else
+    printf("    Apex  [%d] = x%lx  (%.12g, %.12g)\n",
+           t->orient + 3, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+
+  if (b->usesegments) {
+    sdecode(t->tri[6], printsh);
+    if (printsh.ss != m->dummysub) {
+      printf("    [6] = x%lx  %d\n", (unsigned long) printsh.ss,
+             printsh.ssorient);
+    }
+    sdecode(t->tri[7], printsh);
+    if (printsh.ss != m->dummysub) {
+      printf("    [7] = x%lx  %d\n", (unsigned long) printsh.ss,
+             printsh.ssorient);
+    }
+    sdecode(t->tri[8], printsh);
+    if (printsh.ss != m->dummysub) {
+      printf("    [8] = x%lx  %d\n", (unsigned long) printsh.ss,
+             printsh.ssorient);
+    }
+  }
+
+  if (b->vararea) {
+    printf("    Area constraint:  %.4g\n", areabound(*t));
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  printsubseg()   Print out the details of an oriented subsegment.         */
+/*                                                                           */
+/*  I originally wrote this procedure to simplify debugging; it can be       */
+/*  called directly from the debugger, and presents information about an     */
+/*  oriented subsegment in digestible form.  It's also used when the highest */
+/*  level of verbosity (`-VVV') is specified.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void printsubseg(struct mesh *m, struct behavior *b, struct osub *s)
+#else /* not ANSI_DECLARATORS */
+void printsubseg(m, b, s)
+struct mesh *m;
+struct behavior *b;
+struct osub *s;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct osub printsh;
+  struct otri printtri;
+  vertex printvertex;
+
+  printf("subsegment x%lx with orientation %d and mark %d:\n",
+         (unsigned long) s->ss, s->ssorient, mark(*s));
+  sdecode(s->ss[0], printsh);
+  if (printsh.ss == m->dummysub) {
+    printf("    [0] = No subsegment\n");
+  } else {
+    printf("    [0] = x%lx  %d\n", (unsigned long) printsh.ss,
+           printsh.ssorient);
+  }
+  sdecode(s->ss[1], printsh);
+  if (printsh.ss == m->dummysub) {
+    printf("    [1] = No subsegment\n");
+  } else {
+    printf("    [1] = x%lx  %d\n", (unsigned long) printsh.ss,
+           printsh.ssorient);
+  }
+
+  sorg(*s, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Origin[%d] = NULL\n", 2 + s->ssorient);
+  else
+    printf("    Origin[%d] = x%lx  (%.12g, %.12g)\n",
+           2 + s->ssorient, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+  sdest(*s, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Dest  [%d] = NULL\n", 3 - s->ssorient);
+  else
+    printf("    Dest  [%d] = x%lx  (%.12g, %.12g)\n",
+           3 - s->ssorient, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+
+  decode(s->ss[4], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [4] = Outer space\n");
+  } else {
+    printf("    [4] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+  decode(s->ss[5], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [5] = Outer space\n");
+  } else {
+    printf("    [5] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Debugging routines end here                               *********/
+
+/********* Memory management routines begin here                     *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  poolrestart()   Deallocate all items in a pool.                          */
+/*                                                                           */
+/*  The pool is returned to its starting state, except that no memory is     */
+/*  freed to the operating system.  Rather, the previously allocated blocks  */
+/*  are ready to be reused.                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolrestart(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void poolrestart(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  unsigned long alignptr;
+
+  pool->items = 0;
+  pool->maxitems = 0;
+
+  /* Set the currently active block. */
+  pool->nowblock = pool->firstblock;
+  /* Find the first item in the pool.  Increment by the size of (VOID *). */
+  alignptr = (unsigned long) (pool->nowblock + 1);
+  /* Align the item on an `alignbytes'-byte boundary. */
+  pool->nextitem = (VOID *)
+    (alignptr + (unsigned long) pool->alignbytes -
+     (alignptr % (unsigned long) pool->alignbytes));
+  /* There are lots of unallocated items left in this block. */
+  pool->unallocateditems = pool->itemsfirstblock;
+  /* The stack of deallocated items is empty. */
+  pool->deaditemstack = (VOID *) NULL;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  poolinit()   Initialize a pool of memory for allocation of items.        */
+/*                                                                           */
+/*  This routine initializes the machinery for allocating items.  A `pool'   */
+/*  is created whose records have size at least `bytecount'.  Items will be  */
+/*  allocated in `itemcount'-item blocks.  Each item is assumed to be a      */
+/*  collection of words, and either pointers or floating-point values are    */
+/*  assumed to be the "primary" word type.  (The "primary" word type is used */
+/*  to determine alignment of items.)  If `alignment' isn't zero, all items  */
+/*  will be `alignment'-byte aligned in memory.  `alignment' must be either  */
+/*  a multiple or a factor of the primary word size; powers of two are safe. */
+/*  `alignment' is normally used to create a few unused bits at the bottom   */
+/*  of each item's pointer, in which information may be stored.              */
+/*                                                                           */
+/*  Don't change this routine unless you understand it.                      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolinit(struct memorypool *pool, int bytecount, int itemcount,
+              int firstitemcount, enum wordtype wtype, int alignment)
+#else /* not ANSI_DECLARATORS */
+void poolinit(pool, bytecount, itemcount, firstitemcount, wtype, alignment)
+struct memorypool *pool;
+int bytecount;
+int itemcount;
+int firstitemcount;
+enum wordtype wtype;
+int alignment;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int wordsize;
+
+  /* Initialize values in the pool. */
+  pool->itemwordtype = wtype;
+  wordsize = (pool->itemwordtype == POINTER) ? sizeof(VOID *) : sizeof(REAL);
+  /* Find the proper alignment, which must be at least as large as:   */
+  /*   - The parameter `alignment'.                                   */
+  /*   - The primary word type, to avoid unaligned accesses.          */
+  /*   - sizeof(VOID *), so the stack of dead items can be maintained */
+  /*       without unaligned accesses.                                */
+  if (alignment > wordsize) {
+    pool->alignbytes = alignment;
+  } else {
+    pool->alignbytes = wordsize;
+  }
+  if (sizeof(VOID *) > pool->alignbytes) {
+    pool->alignbytes = sizeof(VOID *);
+  }
+  pool->itemwords = ((bytecount + pool->alignbytes - 1) / pool->alignbytes)
+                  * (pool->alignbytes / wordsize);
+  pool->itembytes = pool->itemwords * wordsize;
+  pool->itemsperblock = itemcount;
+  if (firstitemcount == 0) {
+    pool->itemsfirstblock = itemcount;
+  } else {
+    pool->itemsfirstblock = firstitemcount;
+  }
+
+  /* Allocate a block of items.  Space for `itemsfirstblock' items and one  */
+  /*   pointer (to point to the next block) are allocated, as well as space */
+  /*   to ensure alignment of the items.                                    */
+  pool->firstblock = (VOID **)
+    trimalloc(pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) +
+              pool->alignbytes);
+  /* Set the next block pointer to NULL. */
+  *(pool->firstblock) = (VOID *) NULL;
+  poolrestart(pool);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  pooldeinit()   Free to the operating system all memory taken by a pool.  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void pooldeinit(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void pooldeinit(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  while (pool->firstblock != (VOID **) NULL) {
+    pool->nowblock = (VOID **) *(pool->firstblock);
+    trifree((VOID *) pool->firstblock);
+    pool->firstblock = pool->nowblock;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  poolalloc()   Allocate space for an item.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+VOID *poolalloc(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+VOID *poolalloc(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID *newitem;
+  VOID **newblock;
+  unsigned long alignptr;
+
+  /* First check the linked list of dead items.  If the list is not   */
+  /*   empty, allocate an item from the list rather than a fresh one. */
+  if (pool->deaditemstack != (VOID *) NULL) {
+    newitem = pool->deaditemstack;               /* Take first item in list. */
+    pool->deaditemstack = * (VOID **) pool->deaditemstack;
+  } else {
+    /* Check if there are any free items left in the current block. */
+    if (pool->unallocateditems == 0) {
+      /* Check if another block must be allocated. */
+      if (*(pool->nowblock) == (VOID *) NULL) {
+        /* Allocate a new block of items, pointed to by the previous block. */
+        newblock = (VOID **) trimalloc(pool->itemsperblock * pool->itembytes +
+                                       (int) sizeof(VOID *) +
+                                       pool->alignbytes);
+        *(pool->nowblock) = (VOID *) newblock;
+        /* The next block pointer is NULL. */
+        *newblock = (VOID *) NULL;
+      }
+
+      /* Move to the new block. */
+      pool->nowblock = (VOID **) *(pool->nowblock);
+      /* Find the first item in the block.    */
+      /*   Increment by the size of (VOID *). */
+      alignptr = (unsigned long) (pool->nowblock + 1);
+      /* Align the item on an `alignbytes'-byte boundary. */
+      pool->nextitem = (VOID *)
+        (alignptr + (unsigned long) pool->alignbytes -
+         (alignptr % (unsigned long) pool->alignbytes));
+      /* There are lots of unallocated items left in this block. */
+      pool->unallocateditems = pool->itemsperblock;
+    }
+
+    /* Allocate a new item. */
+    newitem = pool->nextitem;
+    /* Advance `nextitem' pointer to next free item in block. */
+    if (pool->itemwordtype == POINTER) {
+      pool->nextitem = (VOID *) ((VOID **) pool->nextitem + pool->itemwords);
+    } else {
+      pool->nextitem = (VOID *) ((REAL *) pool->nextitem + pool->itemwords);
+    }
+    pool->unallocateditems--;
+    pool->maxitems++;
+  }
+  pool->items++;
+  return newitem;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  pooldealloc()   Deallocate space for an item.                            */
+/*                                                                           */
+/*  The deallocated space is stored in a queue for later reuse.              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void pooldealloc(struct memorypool *pool, VOID *dyingitem)
+#else /* not ANSI_DECLARATORS */
+void pooldealloc(pool, dyingitem)
+struct memorypool *pool;
+VOID *dyingitem;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Push freshly killed item onto stack. */
+  *((VOID **) dyingitem) = pool->deaditemstack;
+  pool->deaditemstack = dyingitem;
+  pool->items--;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  traversalinit()   Prepare to traverse the entire list of items.          */
+/*                                                                           */
+/*  This routine is used in conjunction with traverse().                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void traversalinit(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void traversalinit(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  unsigned long alignptr;
+
+  /* Begin the traversal in the first block. */
+  pool->pathblock = pool->firstblock;
+  /* Find the first item in the block.  Increment by the size of (VOID *). */
+  alignptr = (unsigned long) (pool->pathblock + 1);
+  /* Align with item on an `alignbytes'-byte boundary. */
+  pool->pathitem = (VOID *)
+    (alignptr + (unsigned long) pool->alignbytes -
+     (alignptr % (unsigned long) pool->alignbytes));
+  /* Set the number of items left in the current block. */
+  pool->pathitemsleft = pool->itemsfirstblock;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  traverse()   Find the next item in the list.                             */
+/*                                                                           */
+/*  This routine is used in conjunction with traversalinit().  Be forewarned */
+/*  that this routine successively returns all items in the list, including  */
+/*  deallocated ones on the deaditemqueue.  It's up to you to figure out     */
+/*  which ones are actually dead.  Why?  I don't want to allocate extra      */
+/*  space just to demarcate dead items.  It can usually be done more         */
+/*  space-efficiently by a routine that knows something about the structure  */
+/*  of the item.                                                             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+VOID *traverse(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+VOID *traverse(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID *newitem;
+  unsigned long alignptr;
+
+  /* Stop upon exhausting the list of items. */
+  if (pool->pathitem == pool->nextitem) {
+    return (VOID *) NULL;
+  }
+
+  /* Check whether any untraversed items remain in the current block. */
+  if (pool->pathitemsleft == 0) {
+    /* Find the next block. */
+    pool->pathblock = (VOID **) *(pool->pathblock);
+    /* Find the first item in the block.  Increment by the size of (VOID *). */
+    alignptr = (unsigned long) (pool->pathblock + 1);
+    /* Align with item on an `alignbytes'-byte boundary. */
+    pool->pathitem = (VOID *)
+      (alignptr + (unsigned long) pool->alignbytes -
+       (alignptr % (unsigned long) pool->alignbytes));
+    /* Set the number of items left in the current block. */
+    pool->pathitemsleft = pool->itemsperblock;
+  }
+
+  newitem = pool->pathitem;
+  /* Find the next item in the block. */
+  if (pool->itemwordtype == POINTER) {
+    pool->pathitem = (VOID *) ((VOID **) pool->pathitem + pool->itemwords);
+  } else {
+    pool->pathitem = (VOID *) ((REAL *) pool->pathitem + pool->itemwords);
+  }
+  pool->pathitemsleft--;
+  return newitem;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  dummyinit()   Initialize the triangle that fills "outer space" and the   */
+/*                omnipresent subsegment.                                    */
+/*                                                                           */
+/*  The triangle that fills "outer space," called `dummytri', is pointed to  */
+/*  by every triangle and subsegment on a boundary (be it outer or inner) of */
+/*  the triangulation.  Also, `dummytri' points to one of the triangles on   */
+/*  the convex hull (until the holes and concavities are carved), making it  */
+/*  possible to find a starting triangle for point location.                 */
+/*                                                                           */
+/*  The omnipresent subsegment, `dummysub', is pointed to by every triangle  */
+/*  or subsegment that doesn't have a full complement of real subsegments    */
+/*  to point to.                                                             */
+/*                                                                           */
+/*  `dummytri' and `dummysub' are generally required to fulfill only a few   */
+/*  invariants:  their vertices must remain NULL and `dummytri' must always  */
+/*  be bonded (at offset zero) to some triangle on the convex hull of the    */
+/*  mesh, via a boundary edge.  Otherwise, the connections of `dummytri' and */
+/*  `dummysub' may change willy-nilly.  This makes it possible to avoid      */
+/*  writing a good deal of special-case code (in the edge flip, for example) */
+/*  for dealing with the boundary of the mesh, places where no subsegment is */
+/*  present, and so forth.  Other entities are frequently bonded to          */
+/*  `dummytri' and `dummysub' as if they were real mesh entities, with no    */
+/*  harm done.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void dummyinit(struct mesh *m, struct behavior *b, int trianglewords,
+               int subsegwords)
+#else /* not ANSI_DECLARATORS */
+void dummyinit(m, b, trianglewords, subsegwords)
+struct mesh *m;
+struct behavior *b;
+int trianglewords;
+int subsegwords;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  unsigned long alignptr;
+
+  /* Set up `dummytri', the `triangle' that occupies "outer space." */
+  m->dummytribase = (triangle *)
+                    trimalloc(trianglewords * (int) sizeof(triangle) +
+                              m->triangles.alignbytes);
+  /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */
+  alignptr = (unsigned long) m->dummytribase;
+  m->dummytri = (triangle *)
+    (alignptr + (unsigned long) m->triangles.alignbytes -
+     (alignptr % (unsigned long) m->triangles.alignbytes));
+  /* Initialize the three adjoining triangles to be "outer space."  These  */
+  /*   will eventually be changed by various bonding operations, but their */
+  /*   values don't really matter, as long as they can legally be          */
+  /*   dereferenced.                                                       */
+  m->dummytri[0] = (triangle) m->dummytri;
+  m->dummytri[1] = (triangle) m->dummytri;
+  m->dummytri[2] = (triangle) m->dummytri;
+  /* Three NULL vertices. */
+  m->dummytri[3] = (triangle) NULL;
+  m->dummytri[4] = (triangle) NULL;
+  m->dummytri[5] = (triangle) NULL;
+
+  if (b->usesegments) {
+    /* Set up `dummysub', the omnipresent subsegment pointed to by any */
+    /*   triangle side or subsegment end that isn't attached to a real */
+    /*   subsegment.                                                   */
+    m->dummysubbase = (subseg *) trimalloc(subsegwords * (int) sizeof(subseg) +
+                                           m->subsegs.alignbytes);
+    /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */
+    alignptr = (unsigned long) m->dummysubbase;
+    m->dummysub = (subseg *)
+      (alignptr + (unsigned long) m->subsegs.alignbytes -
+       (alignptr % (unsigned long) m->subsegs.alignbytes));
+    /* Initialize the two adjoining subsegments to be the omnipresent      */
+    /*   subsegment.  These will eventually be changed by various bonding  */
+    /*   operations, but their values don't really matter, as long as they */
+    /*   can legally be dereferenced.                                      */
+    m->dummysub[0] = (subseg) m->dummysub;
+    m->dummysub[1] = (subseg) m->dummysub;
+    /* Two NULL vertices. */
+    m->dummysub[2] = (subseg) NULL;
+    m->dummysub[3] = (subseg) NULL;
+    /* Initialize the two adjoining triangles to be "outer space." */
+    m->dummysub[4] = (subseg) m->dummytri;
+    m->dummysub[5] = (subseg) m->dummytri;
+    /* Set the boundary marker to zero. */
+    * (int *) (m->dummysub + 6) = 0;
+
+    /* Initialize the three adjoining subsegments of `dummytri' to be */
+    /*   the omnipresent subsegment.                                  */
+    m->dummytri[6] = (triangle) m->dummysub;
+    m->dummytri[7] = (triangle) m->dummysub;
+    m->dummytri[8] = (triangle) m->dummysub;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  initializevertexpool()   Calculate the size of the vertex data structure */
+/*                           and initialize its memory pool.                 */
+/*                                                                           */
+/*  This routine also computes the `vertexmarkindex' and `vertex2triindex'   */
+/*  indices used to find values within each vertex.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void initializevertexpool(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void initializevertexpool(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int vertexsize;
+
+  /* The index within each vertex at which the boundary marker is found,    */
+  /*   followed by the vertex type.  Ensure the vertex marker is aligned to */
+  /*   a sizeof(int)-byte address.                                          */
+  m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) +
+                        sizeof(int) - 1) /
+                       sizeof(int);
+  vertexsize = (m->vertexmarkindex + 2) * sizeof(int);
+  if (b->poly) {
+    /* The index within each vertex at which a triangle pointer is found.  */
+    /*   Ensure the pointer is aligned to a sizeof(triangle)-byte address. */
+    m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) /
+                         sizeof(triangle);
+    vertexsize = (m->vertex2triindex + 1) * sizeof(triangle);
+  }
+
+  /* Initialize the pool of vertices. */
+  poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK,
+           m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK,
+           (sizeof(REAL) >= sizeof(triangle)) ? FLOATINGPOINT : POINTER, 0);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  initializetrisubpools()   Calculate the sizes of the triangle and        */
+/*                            subsegment data structures and initialize      */
+/*                            their memory pools.                            */
+/*                                                                           */
+/*  This routine also computes the `highorderindex', `elemattribindex', and  */
+/*  `areaboundindex' indices used to find values within each triangle.       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void initializetrisubpools(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void initializetrisubpools(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int trisize;
+
+  /* The index within each triangle at which the extra nodes (above three)  */
+  /*   associated with high order elements are found.  There are three      */
+  /*   pointers to other triangles, three pointers to corners, and possibly */
+  /*   three pointers to subsegments before the extra nodes.                */
+  m->highorderindex = 6 + (b->usesegments * 3);
+  /* The number of bytes occupied by a triangle. */
+  trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) *
+            sizeof(triangle);
+  /* The index within each triangle at which its attributes are found, */
+  /*   where the index is measured in REALs.                           */
+  m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL);
+  /* The index within each triangle at which the maximum area constraint  */
+  /*   is found, where the index is measured in REALs.  Note that if the  */
+  /*   `regionattrib' flag is set, an additional attribute will be added. */
+  m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib;
+  /* If triangle attributes or an area bound are needed, increase the number */
+  /*   of bytes occupied by a triangle.                                      */
+  if (b->vararea) {
+    trisize = (m->areaboundindex + 1) * sizeof(REAL);
+  } else if (m->eextras + b->regionattrib > 0) {
+    trisize = m->areaboundindex * sizeof(REAL);
+  }
+  /* If a Voronoi diagram or triangle neighbor graph is requested, make    */
+  /*   sure there's room to store an integer index in each triangle.  This */
+  /*   integer index can occupy the same space as the subsegment pointers  */
+  /*   or attributes or area constraint or extra nodes.                    */
+  if ((b->voronoi || b->neighbors) &&
+      (trisize < 6 * sizeof(triangle) + sizeof(int))) {
+    trisize = 6 * sizeof(triangle) + sizeof(int);
+  }
+
+  /* Having determined the memory size of a triangle, initialize the pool. */
+  poolinit(&m->triangles, trisize, TRIPERBLOCK,
+           (2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) :
+           TRIPERBLOCK, POINTER, 4);
+
+  if (b->usesegments) {
+    /* Initialize the pool of subsegments.  Take into account all six */
+    /*   pointers and one boundary marker.                            */
+    poolinit(&m->subsegs, 6 * sizeof(triangle) + sizeof(int), SUBSEGPERBLOCK,
+             SUBSEGPERBLOCK, POINTER, 4);
+
+    /* Initialize the "outer space" triangle and omnipresent subsegment. */
+    dummyinit(m, b, m->triangles.itemwords, m->subsegs.itemwords);
+  } else {
+    /* Initialize the "outer space" triangle. */
+    dummyinit(m, b, m->triangles.itemwords, 0);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangledealloc()   Deallocate space for a triangle, marking it dead.    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangledealloc(struct mesh *m, triangle *dyingtriangle)
+#else /* not ANSI_DECLARATORS */
+void triangledealloc(m, dyingtriangle)
+struct mesh *m;
+triangle *dyingtriangle;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Mark the triangle as dead.  This makes it possible to detect dead */
+  /*   triangles when traversing the list of all triangles.            */
+  killtri(dyingtriangle);
+  pooldealloc(&m->triangles, (VOID *) dyingtriangle);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangletraverse()   Traverse the triangles, skipping dead ones.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+triangle *triangletraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+triangle *triangletraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  triangle *newtriangle;
+
+  do {
+    newtriangle = (triangle *) traverse(&m->triangles);
+    if (newtriangle == (triangle *) NULL) {
+      return (triangle *) NULL;
+    }
+  } while (deadtri(newtriangle));                         /* Skip dead ones. */
+  return newtriangle;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  subsegdealloc()   Deallocate space for a subsegment, marking it dead.    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void subsegdealloc(struct mesh *m, subseg *dyingsubseg)
+#else /* not ANSI_DECLARATORS */
+void subsegdealloc(m, dyingsubseg)
+struct mesh *m;
+subseg *dyingsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Mark the subsegment as dead.  This makes it possible to detect dead */
+  /*   subsegments when traversing the list of all subsegments.          */
+  killsubseg(dyingsubseg);
+  pooldealloc(&m->subsegs, (VOID *) dyingsubseg);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  subsegtraverse()   Traverse the subsegments, skipping dead ones.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+subseg *subsegtraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+subseg *subsegtraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  subseg *newsubseg;
+
+  do {
+    newsubseg = (subseg *) traverse(&m->subsegs);
+    if (newsubseg == (subseg *) NULL) {
+      return (subseg *) NULL;
+    }
+  } while (deadsubseg(newsubseg));                        /* Skip dead ones. */
+  return newsubseg;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertexdealloc()   Deallocate space for a vertex, marking it dead.        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexdealloc(struct mesh *m, vertex dyingvertex)
+#else /* not ANSI_DECLARATORS */
+void vertexdealloc(m, dyingvertex)
+struct mesh *m;
+vertex dyingvertex;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Mark the vertex as dead.  This makes it possible to detect dead */
+  /*   vertices when traversing the list of all vertices.            */
+  setvertextype(dyingvertex, DEADVERTEX);
+  pooldealloc(&m->vertices, (VOID *) dyingvertex);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertextraverse()   Traverse the vertices, skipping dead ones.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+vertex vertextraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+vertex vertextraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex newvertex;
+
+  do {
+    newvertex = (vertex) traverse(&m->vertices);
+    if (newvertex == (vertex) NULL) {
+      return (vertex) NULL;
+    }
+  } while (vertextype(newvertex) == DEADVERTEX);          /* Skip dead ones. */
+  return newvertex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  badsubsegdealloc()   Deallocate space for a bad subsegment, marking it   */
+/*                       dead.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void badsubsegdealloc(struct mesh *m, struct badsubseg *dyingseg)
+#else /* not ANSI_DECLARATORS */
+void badsubsegdealloc(m, dyingseg)
+struct mesh *m;
+struct badsubseg *dyingseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Set subsegment's origin to NULL.  This makes it possible to detect dead */
+  /*   subsegments when traversing the list of all encroached subsegments.   */
+  dyingseg->subsegorg = (vertex) NULL;
+  pooldealloc(&m->badsubsegs, (VOID *) dyingseg);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  badsubsegtraverse()   Traverse the bad subsegments, skipping dead ones.  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+struct badsubseg *badsubsegtraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+struct badsubseg *badsubsegtraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badsubseg *newseg;
+
+  do {
+    newseg = (struct badsubseg *) traverse(&m->badsubsegs);
+    if (newseg == (struct badsubseg *) NULL) {
+      return (struct badsubseg *) NULL;
+    }
+  } while (newseg->subsegorg == (vertex) NULL);           /* Skip dead ones. */
+  return newseg;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  getvertex()   Get a specific vertex, by number, from the list.           */
+/*                                                                           */
+/*  The first vertex is number 'firstnumber'.                                */
+/*                                                                           */
+/*  Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */
+/*  is large).  I don't care to take the trouble to make it work in constant */
+/*  time.                                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+vertex getvertex(struct mesh *m, struct behavior *b, int number)
+#else /* not ANSI_DECLARATORS */
+vertex getvertex(m, b, number)
+struct mesh *m;
+struct behavior *b;
+int number;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID **getblock;
+  vertex foundvertex;
+  unsigned long alignptr;
+  int current;
+
+  getblock = m->vertices.firstblock;
+  current = b->firstnumber;
+
+  /* Find the right block. */
+  if (current + m->vertices.itemsfirstblock <= number) {
+    getblock = (VOID **) *getblock;
+    current += m->vertices.itemsfirstblock;
+    while (current + m->vertices.itemsperblock <= number) {
+      getblock = (VOID **) *getblock;
+      current += m->vertices.itemsperblock;
+    }
+  }
+
+  /* Now find the right vertex. */
+  alignptr = (unsigned long) (getblock + 1);
+  foundvertex = (vertex) (alignptr + (unsigned long) m->vertices.alignbytes -
+                          (alignptr % (unsigned long) m->vertices.alignbytes));
+  while (current < number) {
+    foundvertex += m->vertices.itemwords;
+    current++;
+  }
+  return foundvertex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangledeinit()   Free all remaining allocated memory.                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangledeinit(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void triangledeinit(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  pooldeinit(&m->triangles);
+  trifree((VOID *) m->dummytribase);
+  if (b->usesegments) {
+    pooldeinit(&m->subsegs);
+    trifree((VOID *) m->dummysubbase);
+  }
+  pooldeinit(&m->vertices);
+#ifndef CDT_ONLY
+  if (b->quality) {
+    pooldeinit(&m->badsubsegs);
+    if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
+      pooldeinit(&m->badtriangles);
+      pooldeinit(&m->flipstackers);
+    }
+  }
+#endif /* not CDT_ONLY */
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Memory management routines end here                       *********/
+
+/********* Constructors begin here                                   *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  maketriangle()   Create a new triangle with orientation zero.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri)
+#else /* not ANSI_DECLARATORS */
+void maketriangle(m, b, newotri)
+struct mesh *m;
+struct behavior *b;
+struct otri *newotri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int i;
+
+  newotri->tri = (triangle *) poolalloc(&m->triangles);
+  /* Initialize the three adjoining triangles to be "outer space". */
+  newotri->tri[0] = (triangle) m->dummytri;
+  newotri->tri[1] = (triangle) m->dummytri;
+  newotri->tri[2] = (triangle) m->dummytri;
+  /* Three NULL vertices. */
+  newotri->tri[3] = (triangle) NULL;
+  newotri->tri[4] = (triangle) NULL;
+  newotri->tri[5] = (triangle) NULL;
+  if (b->usesegments) {
+    /* Initialize the three adjoining subsegments to be the omnipresent */
+    /*   subsegment.                                                    */
+    newotri->tri[6] = (triangle) m->dummysub;
+    newotri->tri[7] = (triangle) m->dummysub;
+    newotri->tri[8] = (triangle) m->dummysub;
+  }
+  for (i = 0; i < m->eextras; i++) {
+    setelemattribute(*newotri, i, 0.0);
+  }
+  if (b->vararea) {
+    setareabound(*newotri, -1.0);
+  }
+
+  newotri->orient = 0;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  makesubseg()   Create a new subsegment with orientation zero.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void makesubseg(struct mesh *m, struct osub *newsubseg)
+#else /* not ANSI_DECLARATORS */
+void makesubseg(m, newsubseg)
+struct mesh *m;
+struct osub *newsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  newsubseg->ss = (subseg *) poolalloc(&m->subsegs);
+  /* Initialize the two adjoining subsegments to be the omnipresent */
+  /*   subsegment.                                                  */
+  newsubseg->ss[0] = (subseg) m->dummysub;
+  newsubseg->ss[1] = (subseg) m->dummysub;
+  /* Two NULL vertices. */
+  newsubseg->ss[2] = (subseg) NULL;
+  newsubseg->ss[3] = (subseg) NULL;
+  /* Initialize the two adjoining triangles to be "outer space." */
+  newsubseg->ss[4] = (subseg) m->dummytri;
+  newsubseg->ss[5] = (subseg) m->dummytri;
+  /* Set the boundary marker to zero. */
+  setmark(*newsubseg, 0);
+
+  newsubseg->ssorient = 0;
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Constructors end here                                     *********/
+
+/********* Geometric primitives begin here                           *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/* The adaptive exact arithmetic geometric predicates implemented herein are */
+/*   described in detail in my paper, "Adaptive Precision Floating-Point     */
+/*   Arithmetic and Fast Robust Geometric Predicates."  See the header for a */
+/*   full citation.                                                          */
+
+/* Which of the following two methods of finding the absolute values is      */
+/*   fastest is compiler-dependent.  A few compilers can inline and optimize */
+/*   the fabs() call; but most will incur the overhead of a function call,   */
+/*   which is disastrously slow.  A faster way on IEEE machines might be to  */
+/*   mask the appropriate bit, but that's difficult to do in C without       */
+/*   forcing the value to be stored to memory (rather than be kept in the    */
+/*   register to which the optimizer assigned it).                           */
+
+#define Absolute(a)  ((a) >= 0.0 ? (a) : -(a))
+/* #define Absolute(a)  fabs(a) */
+
+/* Many of the operations are broken up into two pieces, a main part that    */
+/*   performs an approximate operation, and a "tail" that computes the       */
+/*   roundoff error of that operation.                                       */
+/*                                                                           */
+/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),    */
+/*   Split(), and Two_Product() are all implemented as described in the      */
+/*   reference.  Each of these macros requires certain variables to be       */
+/*   defined in the calling routine.  The variables `bvirt', `c', `abig',    */
+/*   `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because   */
+/*   they store the result of an operation that may incur roundoff error.    */
+/*   The input parameter `x' (or the highest numbered `x_' parameter) must   */
+/*   also be declared `INEXACT'.                                             */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+  bvirt = x - a; \
+  y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+  x = (REAL) (a + b); \
+  Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Two_Sum_Tail(a, b, x, y) \
+  bvirt = (REAL) (x - a); \
+  avirt = x - bvirt; \
+  bround = b - bvirt; \
+  around = a - avirt; \
+  y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+  x = (REAL) (a + b); \
+  Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+  bvirt = (REAL) (a - x); \
+  avirt = x + bvirt; \
+  bround = bvirt - b; \
+  around = a - avirt; \
+  y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+  x = (REAL) (a - b); \
+  Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+  c = (REAL) (splitter * a); \
+  abig = (REAL) (c - a); \
+  ahi = c - abig; \
+  alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+  Split(a, ahi, alo); \
+  Split(b, bhi, blo); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+  x = (REAL) (a * b); \
+  Two_Product_Tail(a, b, x, y)
+
+/* Two_Product_Presplit() is Two_Product() where one of the inputs has       */
+/*   already been split.  Avoids redundant splitting.                        */
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+  x = (REAL) (a * b); \
+  Split(a, ahi, alo); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+/* Square() can be done more quickly than Two_Product().                     */
+
+#define Square_Tail(a, x, y) \
+  Split(a, ahi, alo); \
+  err1 = x - (ahi * ahi); \
+  err3 = err1 - ((ahi + ahi) * alo); \
+  y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+  x = (REAL) (a * a); \
+  Square_Tail(a, x, y)
+
+/* Macros for summing expansions of various fixed lengths.  These are all    */
+/*   unrolled versions of Expansion_Sum().                                   */
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+  Two_Sum(a0, b , _i, x0); \
+  Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+  Two_Diff(a0, b , _i, x0); \
+  Two_Sum( a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+  Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+  Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+  Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+  Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+/* Macro for multiplying a two-component expansion by a single component.    */
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+  Split(b, bhi, blo); \
+  Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+  Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x1); \
+  Fast_Two_Sum(_j, _k, x3, x2)
+
+/*****************************************************************************/
+/*                                                                           */
+/*  exactinit()   Initialize the variables used for exact arithmetic.        */
+/*                                                                           */
+/*  `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in   */
+/*  floating-point arithmetic.  `epsilon' bounds the relative roundoff       */
+/*  error.  It is used for floating-point error analysis.                    */
+/*                                                                           */
+/*  `splitter' is used to split floating-point numbers into two half-        */
+/*  length significands for exact multiplication.                            */
+/*                                                                           */
+/*  I imagine that a highly optimizing compiler might be too smart for its   */
+/*  own good, and somehow cause this routine to fail, if it pretends that    */
+/*  floating-point arithmetic is too much like real arithmetic.              */
+/*                                                                           */
+/*  Don't change this routine unless you fully understand it.                */
+/*                                                                           */
+/*****************************************************************************/
+
+void exactinit()
+{
+  REAL half;
+  REAL check, lastcheck;
+  int every_other;
+#ifdef LINUX
+  int cword;
+#endif /* LINUX */
+
+#ifdef CPU86
+#ifdef SINGLE
+  _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */
+#else /* not SINGLE */
+  _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
+#endif /* not SINGLE */
+#endif /* CPU86 */
+#ifdef LINUX
+#ifdef SINGLE
+  /*  cword = 4223; */
+  cword = 4210;                 /* set FPU control word for single precision */
+#else /* not SINGLE */
+  /*  cword = 4735; */
+  cword = 4722;                 /* set FPU control word for double precision */
+#endif /* not SINGLE */
+  _FPU_SETCW(cword);
+#endif /* LINUX */
+
+  every_other = 1;
+  half = 0.5;
+  epsilon = 1.0;
+  splitter = 1.0;
+  check = 1.0;
+  /* Repeatedly divide `epsilon' by two until it is too small to add to      */
+  /*   one without causing roundoff.  (Also check if the sum is equal to     */
+  /*   the previous sum, for machines that round up instead of using exact   */
+  /*   rounding.  Not that these routines will work on such machines.)       */
+  do {
+    lastcheck = check;
+    epsilon *= half;
+    if (every_other) {
+      splitter *= 2.0;
+    }
+    every_other = !every_other;
+    check = 1.0 + epsilon;
+  } while ((check != 1.0) && (check != lastcheck));
+  splitter += 1.0;
+  /* Error bounds for orientation and incircle tests. */
+  resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+  ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+  ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+  ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+  iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+  iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+  iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+  o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+  o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+  o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum_zeroelim()   Sum two expansions, eliminating zero     */
+/*                                  components from the output expansion.    */
+/*                                                                           */
+/*  Sets h = e + f.  See my Robust Predicates paper for details.             */
+/*                                                                           */
+/*  If round-to-even is used (as with IEEE 754), maintains the strongly      */
+/*  nonoverlapping property.  (That is, if e is strongly nonoverlapping, h   */
+/*  will be also.)  Does NOT maintain the nonoverlapping or nonadjacent      */
+/*  properties.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h)
+#else /* not ANSI_DECLARATORS */
+int fast_expansion_sum_zeroelim(elen, e, flen, f, h)  /* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL hh;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    Q = enow;
+    enow = e[++eindex];
+  } else {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen)) {
+    if ((fnow > enow) == (fnow > -enow)) {
+      Fast_Two_Sum(enow, Q, Qnew, hh);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, hh);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+    while ((eindex < elen) && (findex < flen)) {
+      if ((fnow > enow) == (fnow > -enow)) {
+        Two_Sum(Q, enow, Qnew, hh);
+        enow = e[++eindex];
+      } else {
+        Two_Sum(Q, fnow, Qnew, hh);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      if (hh != 0.0) {
+        h[hindex++] = hh;
+      }
+    }
+  }
+  while (eindex < elen) {
+    Two_Sum(Q, enow, Qnew, hh);
+    enow = e[++eindex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  while (findex < flen) {
+    Two_Sum(Q, fnow, Qnew, hh);
+    fnow = f[++findex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion_zeroelim()   Multiply an expansion by a scalar,          */
+/*                               eliminating zero components from the        */
+/*                               output expansion.                           */
+/*                                                                           */
+/*  Sets h = be.  See my Robust Predicates paper for details.                */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
+#else /* not ANSI_DECLARATORS */
+int scale_expansion_zeroelim(elen, e, b, h)   /* e and h cannot be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  INEXACT REAL Q, sum;
+  REAL hh;
+  INEXACT REAL product1;
+  REAL product0;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+
+  Split(b, bhi, blo);
+  Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+  hindex = 0;
+  if (hh != 0) {
+    h[hindex++] = hh;
+  }
+  for (eindex = 1; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+    Two_Sum(Q, product0, sum, hh);
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+    Fast_Two_Sum(product1, sum, Q, hh);
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  estimate()   Produce a one-word estimate of an expansion's value.        */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL estimate(int elen, REAL *e)
+#else /* not ANSI_DECLARATORS */
+REAL estimate(elen, e)
+int elen;
+REAL *e;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL Q;
+  int eindex;
+
+  Q = e[0];
+  for (eindex = 1; eindex < elen; eindex++) {
+    Q += e[eindex];
+  }
+  return Q;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  counterclockwise()   Return a positive value if the points pa, pb, and   */
+/*                       pc occur in counterclockwise order; a negative      */
+/*                       value if they occur in clockwise order; and zero    */
+/*                       if they are collinear.  The result is also a rough  */
+/*                       approximation of twice the signed area of the       */
+/*                       triangle defined by the three points.               */
+/*                                                                           */
+/*  Uses exact arithmetic if necessary to ensure a correct answer.  The      */
+/*  result returned is the determinant of a matrix.  This determinant is     */
+/*  computed adaptively, in the sense that exact arithmetic is used only to  */
+/*  the degree it is needed to ensure that the returned value has the        */
+/*  correct sign.  Hence, this function is usually quite fast, but will run  */
+/*  more slowly when the input points are collinear or nearly so.            */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum)
+#else /* not ANSI_DECLARATORS */
+REAL counterclockwiseadapt(pa, pb, pc, detsum)
+vertex pa;
+vertex pb;
+vertex pc;
+REAL detsum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  INEXACT REAL acx, acy, bcx, bcy;
+  REAL acxtail, acytail, bcxtail, bcytail;
+  INEXACT REAL detleft, detright;
+  REAL detlefttail, detrighttail;
+  REAL det, errbound;
+  REAL B[4], C1[8], C2[12], D[16];
+  INEXACT REAL B3;
+  int C1length, C2length, Dlength;
+  REAL u[4];
+  INEXACT REAL u3;
+  INEXACT REAL s1, t1;
+  REAL s0, t0;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  acx = (REAL) (pa[0] - pc[0]);
+  bcx = (REAL) (pb[0] - pc[0]);
+  acy = (REAL) (pa[1] - pc[1]);
+  bcy = (REAL) (pb[1] - pc[1]);
+
+  Two_Product(acx, bcy, detleft, detlefttail);
+  Two_Product(acy, bcx, detright, detrighttail);
+
+  Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
+               B3, B[2], B[1], B[0]);
+  B[3] = B3;
+
+  det = estimate(4, B);
+  errbound = ccwerrboundB * detsum;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+  Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+  Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+  Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+  if ((acxtail == 0.0) && (acytail == 0.0)
+      && (bcxtail == 0.0) && (bcytail == 0.0)) {
+    return det;
+  }
+
+  errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+  det += (acx * bcytail + bcy * acxtail)
+       - (acy * bcxtail + bcx * acytail);
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Product(acxtail, bcy, s1, s0);
+  Two_Product(acytail, bcx, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+  Two_Product(acx, bcytail, s1, s0);
+  Two_Product(acy, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+  Two_Product(acxtail, bcytail, s1, s0);
+  Two_Product(acytail, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+  return(D[Dlength - 1]);
+}
+
+#ifdef ANSI_DECLARATORS
+REAL counterclockwise(struct mesh *m, struct behavior *b,
+                      vertex pa, vertex pb, vertex pc)
+#else /* not ANSI_DECLARATORS */
+REAL counterclockwise(m, b, pa, pb, pc)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL detleft, detright, det;
+  REAL detsum, errbound;
+
+  m->counterclockcount++;
+
+  detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+  detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+  det = detleft - detright;
+
+  if (b->noexact) {
+    return det;
+  }
+
+  if (detleft > 0.0) {
+    if (detright <= 0.0) {
+      return det;
+    } else {
+      detsum = detleft + detright;
+    }
+  } else if (detleft < 0.0) {
+    if (detright >= 0.0) {
+      return det;
+    } else {
+      detsum = -detleft - detright;
+    }
+  } else {
+    return det;
+  }
+
+  errbound = ccwerrboundA * detsum;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  return counterclockwiseadapt(pa, pb, pc, detsum);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  incircle()   Return a positive value if the point pd lies inside the     */
+/*               circle passing through pa, pb, and pc; a negative value if  */
+/*               it lies outside; and zero if the four points are cocircular.*/
+/*               The points pa, pb, and pc must be in counterclockwise       */
+/*               order, or the sign of the result will be reversed.          */
+/*                                                                           */
+/*  Uses exact arithmetic if necessary to ensure a correct answer.  The      */
+/*  result returned is the determinant of a matrix.  This determinant is     */
+/*  computed adaptively, in the sense that exact arithmetic is used only to  */
+/*  the degree it is needed to ensure that the returned value has the        */
+/*  correct sign.  Hence, this function is usually quite fast, but will run  */
+/*  more slowly when the input points are cocircular or nearly so.           */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent)
+#else /* not ANSI_DECLARATORS */
+REAL incircleadapt(pa, pb, pc, pd, permanent)
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL permanent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+  int axbclen, axxbclen, aybclen, ayybclen, alen;
+  REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+  int bxcalen, bxxcalen, bycalen, byycalen, blen;
+  REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+  int cxablen, cxxablen, cyablen, cyyablen, clen;
+  REAL abdet[64];
+  int ablen;
+  REAL fin1[1152], fin2[1152];
+  REAL *finnow, *finother, *finswap;
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+  INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+  REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+  REAL aa[4], bb[4], cc[4];
+  INEXACT REAL aa3, bb3, cc3;
+  INEXACT REAL ti1, tj1;
+  REAL ti0, tj0;
+  REAL u[4], v[4];
+  INEXACT REAL u3, v3;
+  REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
+  REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
+  int temp8len, temp16alen, temp16blen, temp16clen;
+  int temp32alen, temp32blen, temp48len, temp64len;
+  REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+  int axtbblen, axtcclen, aytbblen, aytcclen;
+  REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+  int bxtaalen, bxtcclen, bytaalen, bytcclen;
+  REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+  int cxtaalen, cxtbblen, cytaalen, cytbblen;
+  REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+  int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
+  REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+  int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+  REAL axtbctt[8], aytbctt[8], bxtcatt[8];
+  REAL bytcatt[8], cxtabtt[8], cytabtt[8];
+  int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+  REAL abt[8], bct[8], cat[8];
+  int abtlen, bctlen, catlen;
+  REAL abtt[4], bctt[4], catt[4];
+  int abttlen, bcttlen, cattlen;
+  INEXACT REAL abtt3, bctt3, catt3;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+  axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+  aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+  ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+  alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+  bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+  bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+  byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+  blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+  cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+  cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+  cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+  clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = iccerrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) {
+    return det;
+  }
+
+  errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+  det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
+                                     - (bdy * cdxtail + cdx * bdytail))
+          + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+       + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
+                                     - (cdy * adxtail + adx * cdytail))
+          + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+       + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
+                                     - (ady * bdxtail + bdx * adytail))
+          + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if ((bdxtail != 0.0) || (bdytail != 0.0)
+      || (cdxtail != 0.0) || (cdytail != 0.0)) {
+    Square(adx, adxadx1, adxadx0);
+    Square(ady, adyady1, adyady0);
+    Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+    aa[3] = aa3;
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)
+      || (adxtail != 0.0) || (adytail != 0.0)) {
+    Square(bdx, bdxbdx1, bdxbdx0);
+    Square(bdy, bdybdy1, bdybdy0);
+    Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+    bb[3] = bb3;
+  }
+  if ((adxtail != 0.0) || (adytail != 0.0)
+      || (bdxtail != 0.0) || (bdytail != 0.0)) {
+    Square(cdx, cdxcdx1, cdxcdx0);
+    Square(cdy, cdycdy1, cdycdy0);
+    Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+    cc[3] = cc3;
+  }
+
+  if (adxtail != 0.0) {
+    axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+    temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
+                                          temp16a);
+
+    axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+    temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+    axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+    temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (adytail != 0.0) {
+    aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+    temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
+                                          temp16a);
+
+    aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+    temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+    aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+    temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdxtail != 0.0) {
+    bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+    temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
+                                          temp16a);
+
+    bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+    temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+    bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+    temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdytail != 0.0) {
+    bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+    temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
+                                          temp16a);
+
+    bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+    temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+    bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+    temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdxtail != 0.0) {
+    cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+    temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
+                                          temp16a);
+
+    cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+    temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+    cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+    temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdytail != 0.0) {
+    cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+    temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
+                                          temp16a);
+
+    cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+    temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+    cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+    temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if ((adxtail != 0.0) || (adytail != 0.0)) {
+    if ((bdxtail != 0.0) || (bdytail != 0.0)
+        || (cdxtail != 0.0) || (cdytail != 0.0)) {
+      Two_Product(bdxtail, cdy, ti1, ti0);
+      Two_Product(bdx, cdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -bdy;
+      Two_Product(cdxtail, negate, ti1, ti0);
+      negate = -bdytail;
+      Two_Product(cdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+      Two_Product(bdxtail, cdytail, ti1, ti0);
+      Two_Product(cdxtail, bdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+      bctt[3] = bctt3;
+      bcttlen = 4;
+    } else {
+      bct[0] = 0.0;
+      bctlen = 1;
+      bctt[0] = 0.0;
+      bcttlen = 1;
+    }
+
+    if (adxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+      axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (cdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
+                                            temp32a);
+      axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+      temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (adytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+      aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
+                                            temp32a);
+      aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+      temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((bdxtail != 0.0) || (bdytail != 0.0)) {
+    if ((cdxtail != 0.0) || (cdytail != 0.0)
+        || (adxtail != 0.0) || (adytail != 0.0)) {
+      Two_Product(cdxtail, ady, ti1, ti0);
+      Two_Product(cdx, adytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -cdy;
+      Two_Product(adxtail, negate, ti1, ti0);
+      negate = -cdytail;
+      Two_Product(adx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+      Two_Product(cdxtail, adytail, ti1, ti0);
+      Two_Product(adxtail, cdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+      catt[3] = catt3;
+      cattlen = 4;
+    } else {
+      cat[0] = 0.0;
+      catlen = 1;
+      catt[0] = 0.0;
+      cattlen = 1;
+    }
+
+    if (bdxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+      bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (adytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
+                                            temp32a);
+      bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+      temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (bdytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+      bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
+                                            temp32a);
+      bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+      temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)) {
+    if ((adxtail != 0.0) || (adytail != 0.0)
+        || (bdxtail != 0.0) || (bdytail != 0.0)) {
+      Two_Product(adxtail, bdy, ti1, ti0);
+      Two_Product(adx, bdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -ady;
+      Two_Product(bdxtail, negate, ti1, ti0);
+      negate = -adytail;
+      Two_Product(bdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+      Two_Product(adxtail, bdytail, ti1, ti0);
+      Two_Product(bdxtail, adytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+      abtt[3] = abtt3;
+      abttlen = 4;
+    } else {
+      abt[0] = 0.0;
+      abtlen = 1;
+      abtt[0] = 0.0;
+      abttlen = 1;
+    }
+
+    if (cdxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+      cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (bdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
+                                            temp32a);
+      cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+      temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (cdytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+      cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
+                                            temp32a);
+      cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+      temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+
+  return finnow[finlength - 1];
+}
+
+#ifdef ANSI_DECLARATORS
+REAL incircle(struct mesh *m, struct behavior *b,
+              vertex pa, vertex pb, vertex pc, vertex pd)
+#else /* not ANSI_DECLARATORS */
+REAL incircle(m, b, pa, pb, pc, pd)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL alift, blift, clift;
+  REAL det;
+  REAL permanent, errbound;
+
+  m->incirclecount++;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+  alift = adx * adx + ady * ady;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+  blift = bdx * bdx + bdy * bdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+  clift = cdx * cdx + cdy * cdy;
+
+  det = alift * (bdxcdy - cdxbdy)
+      + blift * (cdxady - adxcdy)
+      + clift * (adxbdy - bdxady);
+
+  if (b->noexact) {
+    return det;
+  }
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+            + (Absolute(cdxady) + Absolute(adxcdy)) * blift
+            + (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+  errbound = iccerrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    return det;
+  }
+
+  return incircleadapt(pa, pb, pc, pd, permanent);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient3d()   Return a positive value if the point pd lies below the      */
+/*               plane passing through pa, pb, and pc; "below" is defined so */
+/*               that pa, pb, and pc appear in counterclockwise order when   */
+/*               viewed from above the plane.  Returns a negative value if   */
+/*               pd lies above the plane.  Returns zero if the points are    */
+/*               coplanar.  The result is also a rough approximation of six  */
+/*               times the signed volume of the tetrahedron defined by the   */
+/*               four points.                                                */
+/*                                                                           */
+/*  Uses exact arithmetic if necessary to ensure a correct answer.  The      */
+/*  result returned is the determinant of a matrix.  This determinant is     */
+/*  computed adaptively, in the sense that exact arithmetic is used only to  */
+/*  the degree it is needed to ensure that the returned value has the        */
+/*  correct sign.  Hence, this function is usually quite fast, but will run  */
+/*  more slowly when the input points are coplanar or nearly so.             */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd,
+                   REAL aheight, REAL bheight, REAL cheight, REAL dheight,
+                   REAL permanent)
+#else /* not ANSI_DECLARATORS */
+REAL orient3dadapt(pa, pb, pc, pd,
+                   aheight, bheight, cheight, dheight, permanent)
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL aheight;
+REAL bheight;
+REAL cheight;
+REAL dheight;
+REAL permanent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL adet[8], bdet[8], cdet[8];
+  int alen, blen, clen;
+  REAL abdet[16];
+  int ablen;
+  REAL *finnow, *finother, *finswap;
+  REAL fin1[192], fin2[192];
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL adheighttail, bdheighttail, cdheighttail;
+  INEXACT REAL at_blarge, at_clarge;
+  INEXACT REAL bt_clarge, bt_alarge;
+  INEXACT REAL ct_alarge, ct_blarge;
+  REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+  int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+  INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+  INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
+  REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+  REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
+  INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+  INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
+  REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+  REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
+  REAL bct[8], cat[8], abt[8];
+  int bctlen, catlen, abtlen;
+  INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+  INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+  REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+  REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+  REAL u[4], v[12], w[16];
+  INEXACT REAL u3;
+  int vlength, wlength;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k;
+  REAL _0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+  adheight = (REAL) (aheight - dheight);
+  bdheight = (REAL) (bheight - dheight);
+  cdheight = (REAL) (cheight - dheight);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  alen = scale_expansion_zeroelim(4, bc, adheight, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  blen = scale_expansion_zeroelim(4, ca, bdheight, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  clen = scale_expansion_zeroelim(4, ab, cdheight, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = o3derrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  Two_Diff_Tail(aheight, dheight, adheight, adheighttail);
+  Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail);
+  Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail);
+
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) &&
+      (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) &&
+      (adheighttail == 0.0) &&
+      (bdheighttail == 0.0) &&
+      (cdheighttail == 0.0)) {
+    return det;
+  }
+
+  errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+  det += (adheight * ((bdx * cdytail + cdy * bdxtail) -
+                      (bdy * cdxtail + cdx * bdytail)) +
+          adheighttail * (bdx * cdy - bdy * cdx)) +
+         (bdheight * ((cdx * adytail + ady * cdxtail) -
+                      (cdy * adxtail + adx * cdytail)) +
+          bdheighttail * (cdx * ady - cdy * adx)) +
+         (cdheight * ((adx * bdytail + bdy * adxtail) -
+                      (ady * bdxtail + bdx * adytail)) +
+          cdheighttail * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if (adxtail == 0.0) {
+    if (adytail == 0.0) {
+      at_b[0] = 0.0;
+      at_blen = 1;
+      at_c[0] = 0.0;
+      at_clen = 1;
+    } else {
+      negate = -adytail;
+      Two_Product(negate, bdx, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      Two_Product(adytail, cdx, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    }
+  } else {
+    if (adytail == 0.0) {
+      Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      negate = -adxtail;
+      Two_Product(negate, cdy, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    } else {
+      Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+      Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+      Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
+                   at_blarge, at_b[2], at_b[1], at_b[0]);
+      at_b[3] = at_blarge;
+      at_blen = 4;
+      Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+      Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+      Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
+                   at_clarge, at_c[2], at_c[1], at_c[0]);
+      at_c[3] = at_clarge;
+      at_clen = 4;
+    }
+  }
+  if (bdxtail == 0.0) {
+    if (bdytail == 0.0) {
+      bt_c[0] = 0.0;
+      bt_clen = 1;
+      bt_a[0] = 0.0;
+      bt_alen = 1;
+    } else {
+      negate = -bdytail;
+      Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    }
+  } else {
+    if (bdytail == 0.0) {
+      Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      negate = -bdxtail;
+      Two_Product(negate, ady, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    } else {
+      Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+      Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+      Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
+                   bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+      bt_c[3] = bt_clarge;
+      bt_clen = 4;
+      Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+      Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+      Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
+                  bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+      bt_a[3] = bt_alarge;
+      bt_alen = 4;
+    }
+  }
+  if (cdxtail == 0.0) {
+    if (cdytail == 0.0) {
+      ct_a[0] = 0.0;
+      ct_alen = 1;
+      ct_b[0] = 0.0;
+      ct_blen = 1;
+    } else {
+      negate = -cdytail;
+      Two_Product(negate, adx, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    }
+  } else {
+    if (cdytail == 0.0) {
+      Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      negate = -cdxtail;
+      Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    } else {
+      Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+      Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+      Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
+                   ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+      ct_a[3] = ct_alarge;
+      ct_alen = 4;
+      Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+      Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+      Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
+                   ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+      ct_b[3] = ct_blarge;
+      ct_blen = 4;
+    }
+  }
+
+  bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+  wlength = scale_expansion_zeroelim(bctlen, bct, adheight, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+  wlength = scale_expansion_zeroelim(catlen, cat, bdheight, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+  wlength = scale_expansion_zeroelim(abtlen, abt, cdheight, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  if (adheighttail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, bc, adheighttail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdheighttail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdheighttail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ab, cdheighttail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if (adxtail != 0.0) {
+    if (bdytail != 0.0) {
+      Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+      Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheight, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdheighttail != 0.0) {
+        Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail,
+                        u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (cdytail != 0.0) {
+      negate = -adxtail;
+      Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+      Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheight, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdheighttail != 0.0) {
+        Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail,
+                        u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (bdxtail != 0.0) {
+    if (cdytail != 0.0) {
+      Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+      Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheight, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adheighttail != 0.0) {
+        Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail,
+                        u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (adytail != 0.0) {
+      negate = -bdxtail;
+      Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+      Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheight, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdheighttail != 0.0) {
+        Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail,
+                        u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (cdxtail != 0.0) {
+    if (adytail != 0.0) {
+      Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+      Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheight, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdheighttail != 0.0) {
+        Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail,
+                        u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (bdytail != 0.0) {
+      negate = -cdxtail;
+      Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+      Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheight, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adheighttail != 0.0) {
+        Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail,
+                        u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+
+  if (adheighttail != 0.0) {
+    wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdheighttail != 0.0) {
+    wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdheighttail != 0.0) {
+    wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  return finnow[finlength - 1];
+}
+
+#ifdef ANSI_DECLARATORS
+REAL orient3d(struct mesh *m, struct behavior *b,
+              vertex pa, vertex pb, vertex pc, vertex pd,
+              REAL aheight, REAL bheight, REAL cheight, REAL dheight)
+#else /* not ANSI_DECLARATORS */
+REAL orient3d(m, b, pa, pb, pc, pd, aheight, bheight, cheight, dheight)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL aheight;
+REAL bheight;
+REAL cheight;
+REAL dheight;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL det;
+  REAL permanent, errbound;
+
+  m->orient3dcount++;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+  adheight = aheight - dheight;
+  bdheight = bheight - dheight;
+  cdheight = cheight - dheight;
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+
+  det = adheight * (bdxcdy - cdxbdy) 
+      + bdheight * (cdxady - adxcdy)
+      + cdheight * (adxbdy - bdxady);
+
+  if (b->noexact) {
+    return det;
+  }
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight)
+            + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight)
+            + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight);
+  errbound = o3derrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    return det;
+  }
+
+  return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight,
+                       permanent);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  nonregular()   Return a positive value if the point pd is incompatible   */
+/*                 with the circle or plane passing through pa, pb, and pc   */
+/*                 (meaning that pd is inside the circle or below the        */
+/*                 plane); a negative value if it is compatible; and zero if */
+/*                 the four points are cocircular/coplanar.  The points pa,  */
+/*                 pb, and pc must be in counterclockwise order, or the sign */
+/*                 of the result will be reversed.                           */
+/*                                                                           */
+/*  If the -w switch is used, the points are lifted onto the parabolic       */
+/*  lifting map, then they are dropped according to their weights, then the  */
+/*  3D orientation test is applied.  If the -W switch is used, the points'   */
+/*  heights are already provided, so the 3D orientation test is applied      */
+/*  directly.  If neither switch is used, the incircle test is applied.      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL nonregular(struct mesh *m, struct behavior *b,
+                vertex pa, vertex pb, vertex pc, vertex pd)
+#else /* not ANSI_DECLARATORS */
+REAL nonregular(m, b, pa, pb, pc, pd)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  if (b->weighted == 0) {
+    return incircle(m, b, pa, pb, pc, pd);
+  } else if (b->weighted == 1) {
+    return orient3d(m, b, pa, pb, pc, pd,
+                    pa[0] * pa[0] + pa[1] * pa[1] - pa[2],
+                    pb[0] * pb[0] + pb[1] * pb[1] - pb[2],
+                    pc[0] * pc[0] + pc[1] * pc[1] - pc[2],
+                    pd[0] * pd[0] + pd[1] * pd[1] - pd[2]);
+  } else {
+    return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  findcircumcenter()   Find the circumcenter of a triangle.                */
+/*                                                                           */
+/*  The result is returned both in terms of x-y coordinates and xi-eta       */
+/*  (barycentric) coordinates.  The xi-eta coordinate system is defined in   */
+/*  terms of the triangle:  the origin of the triangle is the origin of the  */
+/*  coordinate system; the destination of the triangle is one unit along the */
+/*  xi axis; and the apex of the triangle is one unit along the eta axis.    */
+/*  This procedure also returns the square of the length of the triangle's   */
+/*  shortest edge.                                                           */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void findcircumcenter(struct mesh *m, struct behavior *b,
+                      vertex torg, vertex tdest, vertex tapex,
+                      vertex circumcenter, REAL *xi, REAL *eta, REAL *minedge,
+                      int offcenter)
+#else /* not ANSI_DECLARATORS */
+void findcircumcenter(m, b, torg, tdest, tapex, circumcenter, xi, eta, minedge,
+                      offcenter)
+struct mesh *m;
+struct behavior *b;
+vertex torg;
+vertex tdest;
+vertex tapex;
+vertex circumcenter;
+REAL *xi;
+REAL *eta;
+REAL *minedge;
+int offcenter;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL xdo, ydo, xao, yao;
+  REAL dodist, aodist, dadist;
+  REAL denominator;
+  REAL dx, dy, dxoff, dyoff;
+
+  m->circumcentercount++;
+
+  /* Compute the circumcenter of the triangle. */
+  xdo = tdest[0] - torg[0];
+  ydo = tdest[1] - torg[1];
+  xao = tapex[0] - torg[0];
+  yao = tapex[1] - torg[1];
+  dodist = xdo * xdo + ydo * ydo;
+  aodist = xao * xao + yao * yao;
+  dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) +
+           (tdest[1] - tapex[1]) * (tdest[1] - tapex[1]);
+  if (b->noexact) {
+    denominator = 0.5 / (xdo * yao - xao * ydo);
+  } else {
+    /* Use the counterclockwise() routine to ensure a positive (and */
+    /*   reasonably accurate) result, avoiding any possibility of   */
+    /*   division by zero.                                          */
+    denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg);
+    /* Don't count the above as an orientation test. */
+    m->counterclockcount--;
+  }
+  dx = (yao * dodist - ydo * aodist) * denominator;
+  dy = (xdo * aodist - xao * dodist) * denominator;
+
+  /* Find the (squared) length of the triangle's shortest edge.  This   */
+  /*   serves as a conservative estimate of the insertion radius of the */
+  /*   circumcenter's parent.  The estimate is used to ensure that      */
+  /*   the algorithm terminates even if very small angles appear in     */
+  /*   the input PSLG.                                                  */
+  if ((dodist < aodist) && (dodist < dadist)) {
+    *minedge = dodist;
+    if (offcenter && (b->offconstant > 0.0)) {
+      /* Find the position of the off-center, as described by Alper Ungor. */
+      dxoff = 0.5 * xdo - b->offconstant * ydo;
+      dyoff = 0.5 * ydo + b->offconstant * xdo;
+      /* If the off-center is closer to the origin than the */
+      /*   circumcenter, use the off-center instead.        */
+      if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
+        dx = dxoff;
+        dy = dyoff;
+      }
+    }
+  } else if (aodist < dadist) {
+    *minedge = aodist;
+    if (offcenter && (b->offconstant > 0.0)) {
+      dxoff = 0.5 * xao + b->offconstant * yao;
+      dyoff = 0.5 * yao - b->offconstant * xao;
+      /* If the off-center is closer to the origin than the */
+      /*   circumcenter, use the off-center instead.        */
+      if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
+        dx = dxoff;
+        dy = dyoff;
+      }
+    }
+  } else {
+    *minedge = dadist;
+    if (offcenter && (b->offconstant > 0.0)) {
+      dxoff = 0.5 * (tapex[0] - tdest[0]) -
+              b->offconstant * (tapex[1] - tdest[1]);
+      dyoff = 0.5 * (tapex[1] - tdest[1]) +
+              b->offconstant * (tapex[0] - tdest[0]);
+      /* If the off-center is closer to the destination than the */
+      /*   circumcenter, use the off-center instead.             */
+      if (dxoff * dxoff + dyoff * dyoff <
+          (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) {
+        dx = xdo + dxoff;
+        dy = ydo + dyoff;
+      }
+    }
+  }
+
+  circumcenter[0] = torg[0] + dx;
+  circumcenter[1] = torg[1] + dy;
+
+  /* To interpolate vertex attributes for the new vertex inserted at */
+  /*   the circumcenter, define a coordinate system with a xi-axis,  */
+  /*   directed from the triangle's origin to its destination, and   */
+  /*   an eta-axis, directed from its origin to its apex.            */
+  /*   Calculate the xi and eta coordinates of the circumcenter.     */
+  *xi = (yao * dx - xao * dy) * (2.0 * denominator);
+  *eta = (xdo * dy - ydo * dx) * (2.0 * denominator);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Geometric primitives end here                             *********/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangleinit()   Initialize some variables.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangleinit(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+void triangleinit(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  m->vertices.maxitems = m->triangles.maxitems = m->subsegs.maxitems =
+    m->viri.maxitems = m->badsubsegs.maxitems = m->badtriangles.maxitems =
+    m->flipstackers.maxitems = m->splaynodes.maxitems = 0l;
+  m->vertices.itembytes = m->triangles.itembytes = m->subsegs.itembytes =
+    m->viri.itembytes = m->badsubsegs.itembytes = m->badtriangles.itembytes =
+    m->flipstackers.itembytes = m->splaynodes.itembytes = 0;
+  m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */
+  m->undeads = 0;                       /* No eliminated input vertices yet. */
+  m->samples = 1;         /* Point location should take at least one sample. */
+  m->checksegments = 0;   /* There are no segments in the triangulation yet. */
+  m->checkquality = 0;     /* The quality triangulation stage has not begun. */
+  m->incirclecount = m->counterclockcount = m->orient3dcount = 0;
+  m->hyperbolacount = m->circletopcount = m->circumcentercount = 0;
+  randomseed = 1;
+
+  exactinit();                     /* Initialize exact arithmetic constants. */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  randomnation()   Generate a random number between 0 and `choices' - 1.   */
+/*                                                                           */
+/*  This is a simple linear congruential random number generator.  Hence, it */
+/*  is a bad random number generator, but good enough for most randomized    */
+/*  geometric algorithms.                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+unsigned long randomnation(unsigned int choices)
+#else /* not ANSI_DECLARATORS */
+unsigned long randomnation(choices)
+unsigned int choices;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  randomseed = (randomseed * 1366l + 150889l) % 714025l;
+  return randomseed / (714025l / choices + 1);
+}
+
+/********* Mesh quality testing routines begin here                  *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  checkmesh()   Test the mesh for topological consistency.                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void checkmesh(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void checkmesh(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  struct otri oppotri, oppooppotri;
+  vertex triorg, tridest, triapex;
+  vertex oppoorg, oppodest;
+  int horrors;
+  int saveexact;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  /* Temporarily turn on exact arithmetic if it's off. */
+  saveexact = b->noexact;
+  b->noexact = 0;
+  if (!b->quiet) {
+    printf("  Checking consistency of mesh...\n");
+  }
+  horrors = 0;
+  /* Run through the list of triangles, checking each one. */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* Check all three edges of the triangle. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      org(triangleloop, triorg);
+      dest(triangleloop, tridest);
+      if (triangleloop.orient == 0) {       /* Only test for inversion once. */
+        /* Test if the triangle is flat or inverted. */
+        apex(triangleloop, triapex);
+        if (counterclockwise(m, b, triorg, tridest, triapex) <= 0.0) {
+          printf("  !! !! Inverted ");
+          printtriangle(m, b, &triangleloop);
+          horrors++;
+        }
+      }
+      /* Find the neighboring triangle on this edge. */
+      sym(triangleloop, oppotri);
+      if (oppotri.tri != m->dummytri) {
+        /* Check that the triangle's neighbor knows it's a neighbor. */
+        sym(oppotri, oppooppotri);
+        if ((triangleloop.tri != oppooppotri.tri)
+            || (triangleloop.orient != oppooppotri.orient)) {
+          printf("  !! !! Asymmetric triangle-triangle bond:\n");
+          if (triangleloop.tri == oppooppotri.tri) {
+            printf("   (Right triangle, wrong orientation)\n");
+          }
+          printf("    First ");
+          printtriangle(m, b, &triangleloop);
+          printf("    Second (nonreciprocating) ");
+          printtriangle(m, b, &oppotri);
+          horrors++;
+        }
+        /* Check that both triangles agree on the identities */
+        /*   of their shared vertices.                       */
+        org(oppotri, oppoorg);
+        dest(oppotri, oppodest);
+        if ((triorg != oppodest) || (tridest != oppoorg)) {
+          printf("  !! !! Mismatched edge coordinates between two triangles:\n"
+                 );
+          printf("    First mismatched ");
+          printtriangle(m, b, &triangleloop);
+          printf("    Second mismatched ");
+          printtriangle(m, b, &oppotri);
+          horrors++;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+  if (horrors == 0) {
+    if (!b->quiet) {
+      printf("  In my studied opinion, the mesh appears to be consistent.\n");
+    }
+  } else if (horrors == 1) {
+    printf("  !! !! !! !! Precisely one festering wound discovered.\n");
+  } else {
+    printf("  !! !! !! !! %d abominations witnessed.\n", horrors);
+  }
+  /* Restore the status of exact arithmetic. */
+  b->noexact = saveexact;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  checkdelaunay()   Ensure that the mesh is (constrained) Delaunay.        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void checkdelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void checkdelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  struct otri oppotri;
+  struct osub opposubseg;
+  vertex triorg, tridest, triapex;
+  vertex oppoapex;
+  int shouldbedelaunay;
+  int horrors;
+  int saveexact;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Temporarily turn on exact arithmetic if it's off. */
+  saveexact = b->noexact;
+  b->noexact = 0;
+  if (!b->quiet) {
+    printf("  Checking Delaunay property of mesh...\n");
+  }
+  horrors = 0;
+  /* Run through the list of triangles, checking each one. */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* Check all three edges of the triangle. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      org(triangleloop, triorg);
+      dest(triangleloop, tridest);
+      apex(triangleloop, triapex);
+      sym(triangleloop, oppotri);
+      apex(oppotri, oppoapex);
+      /* Only test that the edge is locally Delaunay if there is an   */
+      /*   adjoining triangle whose pointer is larger (to ensure that */
+      /*   each pair isn't tested twice).                             */
+      shouldbedelaunay = (oppotri.tri != m->dummytri) &&
+            !deadtri(oppotri.tri) && (triangleloop.tri < oppotri.tri) &&
+            (triorg != m->infvertex1) && (triorg != m->infvertex2) &&
+            (triorg != m->infvertex3) &&
+            (tridest != m->infvertex1) && (tridest != m->infvertex2) &&
+            (tridest != m->infvertex3) &&
+            (triapex != m->infvertex1) && (triapex != m->infvertex2) &&
+            (triapex != m->infvertex3) &&
+            (oppoapex != m->infvertex1) && (oppoapex != m->infvertex2) &&
+            (oppoapex != m->infvertex3);
+      if (m->checksegments && shouldbedelaunay) {
+        /* If a subsegment separates the triangles, then the edge is */
+        /*   constrained, so no local Delaunay test should be done.  */
+        tspivot(triangleloop, opposubseg);
+        if (opposubseg.ss != m->dummysub){
+          shouldbedelaunay = 0;
+        }
+      }
+      if (shouldbedelaunay) {
+        if (nonregular(m, b, triorg, tridest, triapex, oppoapex) > 0.0) {
+          if (!b->weighted) {
+            printf("  !! !! Non-Delaunay pair of triangles:\n");
+            printf("    First non-Delaunay ");
+            printtriangle(m, b, &triangleloop);
+            printf("    Second non-Delaunay ");
+          } else {
+            printf("  !! !! Non-regular pair of triangles:\n");
+            printf("    First non-regular ");
+            printtriangle(m, b, &triangleloop);
+            printf("    Second non-regular ");
+          }
+          printtriangle(m, b, &oppotri);
+          horrors++;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+  if (horrors == 0) {
+    if (!b->quiet) {
+      printf(
+  "  By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n");
+    }
+  } else if (horrors == 1) {
+    printf(
+         "  !! !! !! !! Precisely one terrifying transgression identified.\n");
+  } else {
+    printf("  !! !! !! !! %d obscenities viewed with horror.\n", horrors);
+  }
+  /* Restore the status of exact arithmetic. */
+  b->noexact = saveexact;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  enqueuebadtriang()   Add a bad triangle data structure to the end of a   */
+/*                       queue.                                              */
+/*                                                                           */
+/*  The queue is actually a set of 64 queues.  I use multiple queues to give */
+/*  priority to smaller angles.  I originally implemented a heap, but the    */
+/*  queues are faster by a larger margin than I'd suspected.                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enqueuebadtriang(struct mesh *m, struct behavior *b,
+                      struct badtriang *badtri)
+#else /* not ANSI_DECLARATORS */
+void enqueuebadtriang(m, b, badtri)
+struct mesh *m;
+struct behavior *b;
+struct badtriang *badtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int queuenumber;
+  int i;
+
+  if (b->verbose > 2) {
+    printf("  Queueing bad triangle:\n");
+    printf("    (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+           badtri->triangorg[0], badtri->triangorg[1],
+           badtri->triangdest[0], badtri->triangdest[1],
+           badtri->triangapex[0], badtri->triangapex[1]);
+  }
+  /* Determine the appropriate queue to put the bad triangle into. */
+  if (badtri->key > 0.6) {
+    queuenumber = (int) (160.0 * (badtri->key - 0.6));
+    if (queuenumber > 63) {
+      queuenumber = 63;
+    }
+  } else {
+    /* It's not a bad angle; put the triangle in the lowest-priority queue. */
+    queuenumber = 0;
+  }
+
+  /* Are we inserting into an empty queue? */
+  if (m->queuefront[queuenumber] == (struct badtriang *) NULL) {
+    /* Yes, we are inserting into an empty queue.     */
+    /*   Will this become the highest-priority queue? */
+    if (queuenumber > m->firstnonemptyq) {
+      /* Yes, this is the highest-priority queue. */
+      m->nextnonemptyq[queuenumber] = m->firstnonemptyq;
+      m->firstnonemptyq = queuenumber;
+    } else {
+      /* No, this is not the highest-priority queue. */
+      /*   Find the queue with next higher priority. */
+      i = queuenumber + 1;
+      while (m->queuefront[i] == (struct badtriang *) NULL) {
+        i++;
+      }
+      /* Mark the newly nonempty queue as following a higher-priority queue. */
+      m->nextnonemptyq[queuenumber] = m->nextnonemptyq[i];
+      m->nextnonemptyq[i] = queuenumber;
+    }
+    /* Put the bad triangle at the beginning of the (empty) queue. */
+    m->queuefront[queuenumber] = badtri;
+  } else {
+    /* Add the bad triangle to the end of an already nonempty queue. */
+    m->queuetail[queuenumber]->nexttriang = badtri;
+  }
+  /* Maintain a pointer to the last triangle of the queue. */
+  m->queuetail[queuenumber] = badtri;
+  /* Newly enqueued bad triangle has no successor in the queue. */
+  badtri->nexttriang = (struct badtriang *) NULL;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  enqueuebadtri()   Add a bad triangle to the end of a queue.              */
+/*                                                                           */
+/*  Allocates a badtriang data structure for the triangle, then passes it to */
+/*  enqueuebadtriang().                                                      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enqueuebadtri(struct mesh *m, struct behavior *b, struct otri *enqtri,
+                   REAL angle, vertex enqapex, vertex enqorg, vertex enqdest)
+#else /* not ANSI_DECLARATORS */
+void enqueuebadtri(m, b, enqtri, angle, enqapex, enqorg, enqdest)
+struct mesh *m;
+struct behavior *b;
+struct otri *enqtri;
+REAL angle;
+vertex enqapex;
+vertex enqorg;
+vertex enqdest;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badtriang *newbad;
+
+  /* Allocate space for the bad triangle. */
+  newbad = (struct badtriang *) poolalloc(&m->badtriangles);
+  newbad->poortri = encode(*enqtri);
+  newbad->key = angle;
+  newbad->triangapex = enqapex;
+  newbad->triangorg = enqorg;
+  newbad->triangdest = enqdest;
+  enqueuebadtriang(m, b, newbad);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  dequeuebadtriang()   Remove a triangle from the front of the queue.      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+struct badtriang *dequeuebadtriang(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+struct badtriang *dequeuebadtriang(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badtriang *result;
+
+  /* If no queues are nonempty, return NULL. */
+  if (m->firstnonemptyq < 0) {
+    return (struct badtriang *) NULL;
+  }
+  /* Find the first triangle of the highest-priority queue. */
+  result = m->queuefront[m->firstnonemptyq];
+  /* Remove the triangle from the queue. */
+  m->queuefront[m->firstnonemptyq] = result->nexttriang;
+  /* If this queue is now empty, note the new highest-priority */
+  /*   nonempty queue.                                         */
+  if (result == m->queuetail[m->firstnonemptyq]) {
+    m->firstnonemptyq = m->nextnonemptyq[m->firstnonemptyq];
+  }
+  return result;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  under60degrees()   Return 1 if the two incident input segments are       */
+/*                     separated by an angle less than 60 degrees;           */
+/*                     0 otherwise.                                          */
+/*                                                                           */
+/*  The two input segments MUST have the same origin.                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+int under60degrees(struct osub *sub1, struct osub *sub2) {
+  vertex segmentapex, v1, v2;
+  REAL dotprod;
+
+  sorg(*sub1, segmentapex);
+  sdest(*sub1, v1);
+  sdest(*sub2, v2);
+  dotprod = (v2[0] - segmentapex[0]) * (v1[0] - segmentapex[0]) +
+            (v2[1] - segmentapex[1]) * (v1[1] - segmentapex[1]);
+  return (dotprod > 0.0) &&
+         (4.0 * dotprod * dotprod >
+          ((v1[0] - segmentapex[0]) * (v1[0] - segmentapex[0]) +
+           (v1[1] - segmentapex[1]) * (v1[1] - segmentapex[1])) *
+          ((v2[0] - segmentapex[0]) * (v2[0] - segmentapex[0]) +
+           (v2[1] - segmentapex[1]) * (v2[1] - segmentapex[1])));
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  clockwiseseg()   Find the next segment clockwise from `thissub' having   */
+/*                   the same origin and return it as `nextsub' if the       */
+/*                   intervening region is inside the domain.                */
+/*                                                                           */
+/*  Returns 1 if the next segment is separated from `thissub' by less than   */
+/*  60 degrees, and the intervening region is inside the domain.             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+int clockwiseseg(struct mesh *m, struct osub *thissub, struct osub *nextsub) {
+  struct otri neighbortri;
+  triangle ptr;           /* Temporary variable used by sym() and stpivot(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  stpivot(*thissub, neighbortri);
+  if (neighbortri.tri == m->dummytri) {
+    return 0;
+  } else {
+    lnextself(neighbortri);
+    tspivot(neighbortri, *nextsub);
+    while (nextsub->ss == m->dummysub) {
+      symself(neighbortri);
+      lnextself(neighbortri);
+      tspivot(neighbortri, *nextsub);
+    }
+    ssymself(*nextsub);
+    return under60degrees(thissub, nextsub);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  counterclockwiseseg()   Find the next segment counterclockwise from      */
+/*                          `thissub' having the same origin and return it   */
+/*                          as `nextsub' if the intervening region is inside */
+/*                          the domain.                                      */
+/*                                                                           */
+/*  Returns 1 if the next segment is separated from `thissub' by less than   */
+/*  60 degrees, and the intervening region is inside the domain.             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+int counterclockwiseseg(struct mesh *m, struct osub *thissub,
+                        struct osub *nextsub) {
+  struct otri neighbortri;
+  struct osub subsym;
+  triangle ptr;           /* Temporary variable used by sym() and stpivot(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  ssym(*thissub, subsym);
+  stpivot(subsym, neighbortri);
+  if (neighbortri.tri == m->dummytri) {
+    return 0;
+  } else {
+    lprevself(neighbortri);
+    tspivot(neighbortri, *nextsub);
+    while (nextsub->ss == m->dummysub) {
+      symself(neighbortri);
+      lprevself(neighbortri);
+      tspivot(neighbortri, *nextsub);
+    }
+    return under60degrees(thissub, nextsub);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+#ifndef CDT_ONLY
+
+/*****************************************************************************/
+/*                                                                           */
+/*  splitpermitted()   Return 1 if `testsubseg' is part of a subsegment      */
+/*                     cluster that is eligible for splitting.               */
+/*                                                                           */
+/*  The term "subsegment cluster" is formally defined in my paper "Mesh      */
+/*  Generation for Domains with Small Angles."  The algorithm that uses this */
+/*  procedure is also described there.                                       */
+/*                                                                           */
+/*  A subsegment cluster is eligible for splitting if (1) it includes a      */
+/*  subsegment whose length is not a power of two, (2) its subsegments are   */
+/*  not all the same length, or (3) no new edge that will be created by      */
+/*  splitting all the subsegments in the cluster has a length shorter than   */
+/*  the insertion radius of the encroaching vertex, whose square is given    */
+/*  as the parameter `iradius'.  Note that the shortest edges created by     */
+/*  splitting a cluster are those whose endpoints are both subsegment        */
+/*  midpoints introduced when the cluster is split.                          */
+/*                                                                           */
+/*  `testsubseg' is also eligible for splitting (and a 1 will be returned)   */
+/*  if it is part of two subsegment clusters; one at its origin and one at   */
+/*  its destination.                                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+int splitpermitted(struct mesh *m, struct osub *testsubseg, REAL iradius) {
+  struct osub cwsubseg, ccwsubseg, cwsubseg2, ccwsubseg2;
+  struct osub testsym;
+  struct osub startsubseg, nowsubseg;
+  vertex suborg, dest1, dest2;
+  REAL nearestpoweroffour, seglength, prevseglength, edgelength;
+  int cwsmall, ccwsmall, cwsmall2, ccwsmall2;
+  int orgcluster, destcluster;
+  int toosmall;
+
+  /* Find the square of the subsegment's length, and the nearest power of */
+  /*   four (which is the square of the nearest power of two to the       */
+  /*   subsegment's length).                                              */
+  sorg(*testsubseg, suborg);
+  sdest(*testsubseg, dest1);
+  seglength = (dest1[0] - suborg[0]) * (dest1[0] - suborg[0]) +
+              (dest1[1] - suborg[1]) * (dest1[1] - suborg[1]);
+  nearestpoweroffour = 1.0;
+  while (seglength > 2.0 * nearestpoweroffour) {
+    nearestpoweroffour *= 4.0;
+  }
+  while (seglength < 0.5 * nearestpoweroffour) {
+    nearestpoweroffour *= 0.25;
+  }
+  /* If the segment's length is not a power of two, the segment */
+  /*   is eligible for splitting.                               */
+  if ((nearestpoweroffour > 1.001 * seglength) ||
+      (nearestpoweroffour < 0.999 * seglength)) {
+    return 1;
+  }
+
+  /* Is `testsubseg' part of a subsegment cluster at its origin? */
+  cwsmall = clockwiseseg(m, testsubseg, &cwsubseg);
+  ccwsmall = cwsmall ? 0 : counterclockwiseseg(m, testsubseg, &ccwsubseg);
+  orgcluster = cwsmall || ccwsmall;
+
+  /* Is `testsubseg' part of a subsegment cluster at its destination? */
+  ssym(*testsubseg, testsym);
+  cwsmall2 = clockwiseseg(m, &testsym, &cwsubseg2);
+  ccwsmall2 = cwsmall2 ? 0 : counterclockwiseseg(m, &testsym, &ccwsubseg2);
+  destcluster = cwsmall2 || ccwsmall2;
+
+  if (orgcluster == destcluster) {
+    /* `testsubseg' is part of two clusters or none, */
+    /*   and thus should be split.                   */
+    return 1;
+  } else if (orgcluster) {
+    /* `testsubseg' is part of a cluster at its origin. */
+    subsegcopy(*testsubseg, startsubseg);
+  } else {
+    /* `testsubseg' is part of a cluster at its destination; switch to */
+    /*   the symmetric case, so we can use the same code to handle it. */
+    subsegcopy(testsym, startsubseg);
+    subsegcopy(cwsubseg2, cwsubseg);
+    subsegcopy(ccwsubseg2, ccwsubseg);
+    cwsmall = cwsmall2;
+    ccwsmall = ccwsmall2;
+  }
+
+  toosmall = 0;
+  if (cwsmall) {
+    /* Check the subsegment(s) clockwise from `testsubseg'. */
+    subsegcopy(startsubseg, nowsubseg);
+    sorg(nowsubseg, suborg);
+    sdest(nowsubseg, dest1);
+    prevseglength = nearestpoweroffour;
+    do {
+      /* Is the next subsegment shorter than `startsubseg'? */
+      sdest(cwsubseg, dest2);
+      seglength = (dest2[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                  (dest2[1] - suborg[1]) * (dest2[1] - suborg[1]);
+      if (nearestpoweroffour > 1.001 * seglength) {
+        /* It's shorter; it's safe to split `startsubseg'. */
+        return 1;
+      }
+      /* If the current and previous subsegments are split to a length  */
+      /*   half that of `startsubseg' (which is a likely consequence if */
+      /*   `startsubseg' is split), what will be (the square of) the    */
+      /*   length of the free edge between the splitting vertices?      */
+      edgelength = 0.5 * nearestpoweroffour *
+                   (1 - (((dest1[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                          (dest1[1] - suborg[1]) * (dest2[1] - suborg[1])) /
+                         sqrt(prevseglength * seglength)));
+      if (edgelength < iradius) {
+        /* If this cluster is split, the new edge dest1-dest2 will be     */
+        /*   smaller than the insertion radius of the encroaching vertex. */
+        /*   Hence, we'd prefer to avoid splitting it if possible.        */
+        toosmall = 1;
+      }
+      if (cwsubseg.ss == startsubseg.ss) {
+        /* We've gone all the way around the vertex.  Split the cluster */
+        /*   if no edges will be too short.                             */
+        return !toosmall;
+      }
+
+      /* Find the next subsegment clockwise around the vertex. */
+      subsegcopy(cwsubseg, nowsubseg);
+      dest1 = dest2;
+      prevseglength = seglength;
+      cwsmall = clockwiseseg(m, &nowsubseg, &cwsubseg);
+    } while (cwsmall);
+
+    /* Prepare to start searching counterclockwise from */
+    /*   the starting subsegment.                       */
+    ccwsmall = counterclockwiseseg(m, &startsubseg, &ccwsubseg);
+  }
+
+  if (ccwsmall) {
+    /* Check the subsegment(s) counterclockwise from `testsubseg'. */
+    subsegcopy(startsubseg, nowsubseg);
+    sorg(nowsubseg, suborg);
+    sdest(nowsubseg, dest1);
+    prevseglength = nearestpoweroffour;
+    do {
+      /* Is the next subsegment shorter than `startsubseg'? */
+      sdest(ccwsubseg, dest2);
+      seglength = (dest2[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                  (dest2[1] - suborg[1]) * (dest2[1] - suborg[1]);
+      if (nearestpoweroffour > 1.001 * seglength) {
+        /* It's shorter; it's safe to split `startsubseg'. */
+        return 1;
+      }
+      /*   half that of `startsubseg' (which is a likely consequence if */
+      /*   `startsubseg' is split), what will be (the square of) the    */
+      /*   length of the free edge between the splitting vertices?      */
+      edgelength = 0.5 * nearestpoweroffour *
+                   (1 - (((dest1[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                          (dest1[1] - suborg[1]) * (dest2[1] - suborg[1])) /
+                         sqrt(prevseglength * seglength)));
+      if (edgelength < iradius) {
+        /* If this cluster is split, the new edge dest1-dest2 will be     */
+        /*   smaller than the insertion radius of the encroaching vertex. */
+        /*   Hence, we'd prefer to avoid splitting it if possible.        */
+        toosmall = 1;
+      }
+      if (ccwsubseg.ss == startsubseg.ss) {
+        /* We've gone all the way around the vertex.  Split the cluster */
+        /*   if no edges will be too short.                             */
+        return !toosmall;
+      }
+
+      /* Find the next subsegment counterclockwise around the vertex. */
+      subsegcopy(ccwsubseg, nowsubseg);
+      dest1 = dest2;
+      prevseglength = seglength;
+      ccwsmall = counterclockwiseseg(m, &nowsubseg, &ccwsubseg);
+    } while (ccwsmall);
+  }
+
+  /* We've found every subsegment in the cluster.  Split the cluster */
+  /*   if no edges will be too short.                                */
+  return !toosmall;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  checkseg4encroach()   Check a subsegment to see if it is encroached; add */
+/*                        it to the list if it is.                           */
+/*                                                                           */
+/*  A subsegment is encroached if there is a vertex in its diametral circle  */
+/*  (that is, the subsegment faces an angle greater than 90 degrees).  This  */
+/*  definition is due to Ruppert.                                            */
+/*                                                                           */
+/*  Returns a nonzero value if the subsegment is encroached.                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+int checkseg4encroach(struct mesh *m, struct behavior *b,
+                      struct osub *testsubseg, REAL iradius)
+#else /* not ANSI_DECLARATORS */
+int checkseg4encroach(m, b, testsubseg, iradius)
+struct mesh *m;
+struct behavior *b;
+struct osub *testsubseg;
+REAL iradius;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri neighbortri;
+  struct osub testsym;
+  struct badsubseg *encroachedseg;
+  REAL dotproduct;
+  int encroached;
+  int sides;
+  int enq;
+  vertex eorg, edest, eapex;
+  triangle ptr;                     /* Temporary variable used by stpivot(). */
+
+  encroached = 0;
+  sides = 0;
+
+  sorg(*testsubseg, eorg);
+  sdest(*testsubseg, edest);
+  /* Check one neighbor of the subsegment. */
+  stpivot(*testsubseg, neighbortri);
+  /* Does the neighbor exist, or is this a boundary edge? */
+  if (neighbortri.tri != m->dummytri) {
+    sides++;
+    /* Find a vertex opposite this subsegment. */
+    apex(neighbortri, eapex);
+    /* Check whether the apex is in the diametral lens of the subsegment */
+    /*   (or the diametral circle, if `nolenses' is set).  A dot product */
+    /*   of two sides of the triangle is used to check whether the angle */
+    /*   at the apex is greater than 120 degrees (for lenses; 90 degrees */
+    /*   for diametral circles).                                         */
+    dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                 (eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
+    if (dotproduct < 0.0) {
+      if (b->nolenses ||
+          (dotproduct * dotproduct >=
+           0.25 * ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
+                   (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
+                  ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                   (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
+        encroached = 1;
+      }
+    }
+  }
+  /* Check the other neighbor of the subsegment. */
+  ssym(*testsubseg, testsym);
+  stpivot(testsym, neighbortri);
+  /* Does the neighbor exist, or is this a boundary edge? */
+  if (neighbortri.tri != m->dummytri) {
+    sides++;
+    /* Find the other vertex opposite this subsegment. */
+    apex(neighbortri, eapex);
+    /* Check whether the apex is in the diametral lens of the subsegment */
+    /*   (or the diametral circle, if `nolenses' is set).                */
+    dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                 (eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
+    if (dotproduct < 0.0) {
+      if (b->nolenses ||
+          (dotproduct * dotproduct >=
+           0.25 * ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
+                   (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
+                  ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                   (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
+        encroached += 2;
+      }
+    }
+  }
+
+  if (encroached && (!b->nobisect || ((b->nobisect == 1) && (sides == 2)))) {
+    /* Decide whether `testsubseg' should be split. */
+    if (iradius > 0.0) {
+      /* The encroaching vertex is a triangle circumcenter, which will be   */
+      /*   rejected.  Hence, `testsubseg' probably should be split, unless  */
+      /*   it is part of a subsegment cluster which, according to the rules */
+      /*   described in my paper "Mesh Generation for Domains with Small    */
+      /*   Angles," should not be split.                                    */
+      enq = splitpermitted(m, testsubseg, iradius);
+    } else {
+      /* The encroaching vertex is an input vertex or was inserted in a */
+      /*   subsegment, so the encroached subsegment must be split.      */
+      enq = 1;
+    }
+    if (enq) {
+      if (b->verbose > 2) {
+        printf(
+          "  Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
+          eorg[0], eorg[1], edest[0], edest[1]);
+      }
+      /* Add the subsegment to the list of encroached subsegments. */
+      /*   Be sure to get the orientation right.                   */
+      encroachedseg = (struct badsubseg *) poolalloc(&m->badsubsegs);
+      if (encroached == 1) {
+        encroachedseg->encsubseg = sencode(*testsubseg);
+        encroachedseg->subsegorg = eorg;
+        encroachedseg->subsegdest = edest;
+      } else {
+        encroachedseg->encsubseg = sencode(testsym);
+        encroachedseg->subsegorg = edest;
+        encroachedseg->subsegdest = eorg;
+      }
+    }
+  }
+
+  return encroached;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  testtriangle()   Test a face for quality measures.                       */
+/*                                                                           */
+/*  Tests a triangle to see if it satisfies the minimum angle condition and  */
+/*  the maximum area condition.  Triangles that aren't up to spec are added  */
+/*  to the bad triangle queue.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void testtriangle(struct mesh *m, struct behavior *b, struct otri *testtri)
+#else /* not ANSI_DECLARATORS */
+void testtriangle(m, b, testtri)
+struct mesh *m;
+struct behavior *b;
+struct otri *testtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri sametesttri;
+  struct osub subseg1, subseg2;
+  vertex torg, tdest, tapex;
+  vertex anglevertex;
+  REAL dxod, dyod, dxda, dyda, dxao, dyao;
+  REAL dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
+  REAL apexlen, orglen, destlen;
+  REAL angle;
+  REAL area;
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*testtri, torg);
+  dest(*testtri, tdest);
+  apex(*testtri, tapex);
+  dxod = torg[0] - tdest[0];
+  dyod = torg[1] - tdest[1];
+  dxda = tdest[0] - tapex[0];
+  dyda = tdest[1] - tapex[1];
+  dxao = tapex[0] - torg[0];
+  dyao = tapex[1] - torg[1];
+  dxod2 = dxod * dxod;
+  dyod2 = dyod * dyod;
+  dxda2 = dxda * dxda;
+  dyda2 = dyda * dyda;
+  dxao2 = dxao * dxao;
+  dyao2 = dyao * dyao;
+  /* Find the lengths of the triangle's three edges. */
+  apexlen = dxod2 + dyod2;
+  orglen = dxda2 + dyda2;
+  destlen = dxao2 + dyao2;
+  if ((apexlen < orglen) && (apexlen < destlen)) {
+    /* The edge opposite the apex is shortest. */
+    /* Find the square of the cosine of the angle at the apex. */
+    angle = dxda * dxao + dyda * dyao;
+    angle = angle * angle / (orglen * destlen);
+    anglevertex = tapex;
+    lnext(*testtri, sametesttri);
+    tspivot(sametesttri, subseg1);
+    lnextself(sametesttri);
+    tspivot(sametesttri, subseg2);
+  } else if (orglen < destlen) {
+    /* The edge opposite the origin is shortest. */
+    /* Find the square of the cosine of the angle at the origin. */
+    angle = dxod * dxao + dyod * dyao;
+    angle = angle * angle / (apexlen * destlen);
+    anglevertex = torg;
+    tspivot(*testtri, subseg1);
+    lprev(*testtri, sametesttri);
+    tspivot(sametesttri, subseg2);
+  } else {
+    /* The edge opposite the destination is shortest. */
+    /* Find the square of the cosine of the angle at the destination. */
+    angle = dxod * dxda + dyod * dyda;
+    angle = angle * angle / (apexlen * orglen);
+    anglevertex = tdest;
+    tspivot(*testtri, subseg1);
+    lnext(*testtri, sametesttri);
+    tspivot(sametesttri, subseg2);
+  }
+
+  /* Check if both edges that form the angle are segments. */
+  if ((subseg1.ss != m->dummysub) && (subseg2.ss != m->dummysub)) {
+    /* The angle is a segment intersection.  Don't add this bad triangle to */
+    /*   the list; there's nothing that can be done about a small angle     */
+    /*   between two segments.                                              */
+    angle = 0.0;
+  }
+
+  /* Check whether the angle is smaller than permitted. */
+  if (angle > b->goodangle) {
+    /* Add this triangle to the list of bad triangles. */
+    enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+    return;
+  }
+
+  if (b->vararea || b->fixedarea || b->usertest) {
+    /* Check whether the area is larger than permitted. */
+    area = 0.5 * (dxod * dyda - dyod * dxda);
+    if (b->fixedarea && (area > b->maxarea)) {
+      /* Add this triangle to the list of bad triangles. */
+      enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+      return;
+    }
+
+    /* Nonpositive area constraints are treated as unconstrained. */
+    if ((b->vararea) && (area > areabound(*testtri)) &&
+        (areabound(*testtri) > 0.0)) {
+      /* Add this triangle to the list of bad triangles. */
+      enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+      return;
+    }
+
+    if (b->usertest) {
+      /* Check whether the user thinks this triangle is too large. */
+      if (triunsuitable(torg, tdest, tapex, area)) {
+        enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+        return;
+      }
+    }
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh quality testing routines end here                    *********/
+
+/********* Point location routines begin here                        *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  makevertexmap()   Construct a mapping from vertices to triangles to      */
+/*                    improve the speed of point location for segment        */
+/*                    insertion.                                             */
+/*                                                                           */
+/*  Traverses all the triangles, and provides each corner of each triangle   */
+/*  with a pointer to that triangle.  Of course, pointers will be            */
+/*  overwritten by other pointers because (almost) each vertex is a corner   */
+/*  of several triangles, but in the end every vertex will point to some     */
+/*  triangle that contains it.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void makevertexmap(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void makevertexmap(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  vertex triorg;
+
+  if (b->verbose) {
+    printf("    Constructing mapping from vertices to triangles.\n");
+  }
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* Check all three vertices of the triangle. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      org(triangleloop, triorg);
+      setvertex2tri(triorg, encode(triangleloop));
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  preciselocate()   Find a triangle or edge containing a given point.      */
+/*                                                                           */
+/*  Begins its search from `searchtri'.  It is important that `searchtri'    */
+/*  be a handle with the property that `searchpoint' is strictly to the left */
+/*  of the edge denoted by `searchtri', or is collinear with that edge and   */
+/*  does not intersect that edge.  (In particular, `searchpoint' should not  */
+/*  be the origin or destination of that edge.)                              */
+/*                                                                           */
+/*  These conditions are imposed because preciselocate() is normally used in */
+/*  one of two situations:                                                   */
+/*                                                                           */
+/*  (1)  To try to find the location to insert a new point.  Normally, we    */
+/*       know an edge that the point is strictly to the left of.  In the     */
+/*       incremental Delaunay algorithm, that edge is a bounding box edge.   */
+/*       In Ruppert's Delaunay refinement algorithm for quality meshing,     */
+/*       that edge is the shortest edge of the triangle whose circumcenter   */
+/*       is being inserted.                                                  */
+/*                                                                           */
+/*  (2)  To try to find an existing point.  In this case, any edge on the    */
+/*       convex hull is a good starting edge.  You must screen out the       */
+/*       possibility that the vertex sought is an endpoint of the starting   */
+/*       edge before you call preciselocate().                               */
+/*                                                                           */
+/*  On completion, `searchtri' is a triangle that contains `searchpoint'.    */
+/*                                                                           */
+/*  This implementation differs from that given by Guibas and Stolfi.  It    */
+/*  walks from triangle to triangle, crossing an edge only if `searchpoint'  */
+/*  is on the other side of the line containing that edge.  After entering   */
+/*  a triangle, there are two edges by which one can leave that triangle.    */
+/*  If both edges are valid (`searchpoint' is on the other side of both      */
+/*  edges), one of the two is chosen by drawing a line perpendicular to      */
+/*  the entry edge (whose endpoints are `forg' and `fdest') passing through  */
+/*  `fapex'.  Depending on which side of this perpendicular `searchpoint'    */
+/*  falls on, an exit edge is chosen.                                        */
+/*                                                                           */
+/*  This implementation is empirically faster than the Guibas and Stolfi     */
+/*  point location routine (which I originally used), which tends to spiral  */
+/*  in toward its target.                                                    */
+/*                                                                           */
+/*  Returns ONVERTEX if the point lies on an existing vertex.  `searchtri'   */
+/*  is a handle whose origin is the existing vertex.                         */
+/*                                                                           */
+/*  Returns ONEDGE if the point lies on a mesh edge.  `searchtri' is a       */
+/*  handle whose primary edge is the edge on which the point lies.           */
+/*                                                                           */
+/*  Returns INTRIANGLE if the point lies strictly within a triangle.         */
+/*  `searchtri' is a handle on the triangle that contains the point.         */
+/*                                                                           */
+/*  Returns OUTSIDE if the point lies outside the mesh.  `searchtri' is a    */
+/*  handle whose primary edge the point is to the right of.  This might      */
+/*  occur when the circumcenter of a triangle falls just slightly outside    */
+/*  the mesh due to floating-point roundoff error.  It also occurs when      */
+/*  seeking a hole or region point that a foolish user has placed outside    */
+/*  the mesh.                                                                */
+/*                                                                           */
+/*  If `stopatsubsegment' is nonzero, the search will stop if it tries to    */
+/*  walk through a subsegment, and will return OUTSIDE.                      */
+/*                                                                           */
+/*  WARNING:  This routine is designed for convex triangulations, and will   */
+/*  not generally work after the holes and concavities have been carved.     */
+/*  However, it can still be used to find the circumcenter of a triangle, as */
+/*  long as the search is begun from the triangle in question.               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum locateresult preciselocate(struct mesh *m, struct behavior *b,
+                                vertex searchpoint, struct otri *searchtri,
+                                int stopatsubsegment)
+#else /* not ANSI_DECLARATORS */
+enum locateresult preciselocate(m, b, searchpoint, searchtri, stopatsubsegment)
+struct mesh *m;
+struct behavior *b;
+vertex searchpoint;
+struct otri *searchtri;
+int stopatsubsegment;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri backtracktri;
+  struct osub checkedge;
+  vertex forg, fdest, fapex;
+  REAL orgorient, destorient;
+  int moveleft;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose > 2) {
+    printf("  Searching for point (%.12g, %.12g).\n",
+           searchpoint[0], searchpoint[1]);
+  }
+  /* Where are we? */
+  org(*searchtri, forg);
+  dest(*searchtri, fdest);
+  apex(*searchtri, fapex);
+  while (1) {
+    if (b->verbose > 2) {
+      printf("    At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+             forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]);
+    }
+    /* Check whether the apex is the point we seek. */
+    if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) {
+      lprevself(*searchtri);
+      return ONVERTEX;
+    }
+    /* Does the point lie on the other side of the line defined by the */
+    /*   triangle edge opposite the triangle's destination?            */
+    destorient = counterclockwise(m, b, forg, fapex, searchpoint);
+    /* Does the point lie on the other side of the line defined by the */
+    /*   triangle edge opposite the triangle's origin?                 */
+    orgorient = counterclockwise(m, b, fapex, fdest, searchpoint);
+    if (destorient > 0.0) {
+      if (orgorient > 0.0) {
+        /* Move left if the inner product of (fapex - searchpoint) and  */
+        /*   (fdest - forg) is positive.  This is equivalent to drawing */
+        /*   a line perpendicular to the line (forg, fdest) and passing */
+        /*   through `fapex', and determining which side of this line   */
+        /*   `searchpoint' falls on.                                    */
+        moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) +
+                   (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0;
+      } else {
+        moveleft = 1;
+      }
+    } else {
+      if (orgorient > 0.0) {
+        moveleft = 0;
+      } else {
+        /* The point we seek must be on the boundary of or inside this */
+        /*   triangle.                                                 */
+        if (destorient == 0.0) {
+          lprevself(*searchtri);
+          return ONEDGE;
+        }
+        if (orgorient == 0.0) {
+          lnextself(*searchtri);
+          return ONEDGE;
+        }
+        return INTRIANGLE;
+      }
+    }
+
+    /* Move to another triangle.  Leave a trace `backtracktri' in case */
+    /*   floating-point roundoff or some such bogey causes us to walk  */
+    /*   off a boundary of the triangulation.                          */
+    if (moveleft) {
+      lprev(*searchtri, backtracktri);
+      fdest = fapex;
+    } else {
+      lnext(*searchtri, backtracktri);
+      forg = fapex;
+    }
+    sym(backtracktri, *searchtri);
+
+    if (m->checksegments && stopatsubsegment) {
+      /* Check for walking through a subsegment. */
+      tspivot(backtracktri, checkedge);
+      if (checkedge.ss != m->dummysub) {
+        /* Go back to the last triangle. */
+        otricopy(backtracktri, *searchtri);
+        return OUTSIDE;
+      }
+    }
+    /* Check for walking right out of the triangulation. */
+    if (searchtri->tri == m->dummytri) {
+      /* Go back to the last triangle. */
+      otricopy(backtracktri, *searchtri);
+      return OUTSIDE;
+    }
+
+    apex(*searchtri, fapex);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  locate()   Find a triangle or edge containing a given point.             */
+/*                                                                           */
+/*  Searching begins from one of:  the input `searchtri', a recently         */
+/*  encountered triangle `recenttri', or from a triangle chosen from a       */
+/*  random sample.  The choice is made by determining which triangle's       */
+/*  origin is closest to the point we are searching for.  Normally,          */
+/*  `searchtri' should be a handle on the convex hull of the triangulation.  */
+/*                                                                           */
+/*  Details on the random sampling method can be found in the Mucke, Saias,  */
+/*  and Zhu paper cited in the header of this code.                          */
+/*                                                                           */
+/*  On completion, `searchtri' is a triangle that contains `searchpoint'.    */
+/*                                                                           */
+/*  Returns ONVERTEX if the point lies on an existing vertex.  `searchtri'   */
+/*  is a handle whose origin is the existing vertex.                         */
+/*                                                                           */
+/*  Returns ONEDGE if the point lies on a mesh edge.  `searchtri' is a       */
+/*  handle whose primary edge is the edge on which the point lies.           */
+/*                                                                           */
+/*  Returns INTRIANGLE if the point lies strictly within a triangle.         */
+/*  `searchtri' is a handle on the triangle that contains the point.         */
+/*                                                                           */
+/*  Returns OUTSIDE if the point lies outside the mesh.  `searchtri' is a    */
+/*  handle whose primary edge the point is to the right of.  This might      */
+/*  occur when the circumcenter of a triangle falls just slightly outside    */
+/*  the mesh due to floating-point roundoff error.  It also occurs when      */
+/*  seeking a hole or region point that a foolish user has placed outside    */
+/*  the mesh.                                                                */
+/*                                                                           */
+/*  WARNING:  This routine is designed for convex triangulations, and will   */
+/*  not generally work after the holes and concavities have been carved.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum locateresult locate(struct mesh *m, struct behavior *b,
+                         vertex searchpoint, struct otri *searchtri)
+#else /* not ANSI_DECLARATORS */
+enum locateresult locate(m, b, searchpoint, searchtri)
+struct mesh *m;
+struct behavior *b;
+vertex searchpoint;
+struct otri *searchtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID **sampleblock;
+  triangle *firsttri;
+  struct otri sampletri;
+  vertex torg, tdest;
+  unsigned long alignptr;
+  REAL searchdist, dist;
+  REAL ahead;
+  long sampleblocks, samplesperblock, samplenum;
+  long triblocks;
+  long i, j;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose > 2) {
+    printf("  Randomly sampling for a triangle near point (%.12g, %.12g).\n",
+           searchpoint[0], searchpoint[1]);
+  }
+  /* Record the distance from the suggested starting triangle to the */
+  /*   point we seek.                                                */
+  org(*searchtri, torg);
+  searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+               (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+  if (b->verbose > 2) {
+    printf("    Boundary triangle has origin (%.12g, %.12g).\n",
+           torg[0], torg[1]);
+  }
+
+  /* If a recently encountered triangle has been recorded and has not been */
+  /*   deallocated, test it as a good starting point.                      */
+  if (m->recenttri.tri != (triangle *) NULL) {
+    if (!deadtri(m->recenttri.tri)) {
+      org(m->recenttri, torg);
+      if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
+        otricopy(m->recenttri, *searchtri);
+        return ONVERTEX;
+      }
+      dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+             (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+      if (dist < searchdist) {
+        otricopy(m->recenttri, *searchtri);
+        searchdist = dist;
+        if (b->verbose > 2) {
+          printf("    Choosing recent triangle with origin (%.12g, %.12g).\n",
+                 torg[0], torg[1]);
+        }
+      }
+    }
+  }
+
+  /* The number of random samples taken is proportional to the cube root of */
+  /*   the number of triangles in the mesh.  The next bit of code assumes   */
+  /*   that the number of triangles increases monotonically.                */
+  while (SAMPLEFACTOR * m->samples * m->samples * m->samples <
+         m->triangles.items) {
+    m->samples++;
+  }
+  triblocks = (m->triangles.maxitems + TRIPERBLOCK - 1) / TRIPERBLOCK;
+  samplesperblock = (m->samples + triblocks - 1) / triblocks;
+  sampleblocks = m->samples / samplesperblock;
+  sampleblock = m->triangles.firstblock;
+  sampletri.orient = 0;
+  for (i = 0; i < sampleblocks; i++) {
+    alignptr = (unsigned long) (sampleblock + 1);
+    firsttri = (triangle *) (alignptr + (unsigned long) m->triangles.alignbytes
+                      - (alignptr % (unsigned long) m->triangles.alignbytes));
+    for (j = 0; j < samplesperblock; j++) {
+      if (i == triblocks - 1) {
+        samplenum = randomnation((unsigned int)
+                                 (m->triangles.maxitems - (i * TRIPERBLOCK)));
+      } else {
+        samplenum = randomnation(TRIPERBLOCK);
+      }
+      sampletri.tri = (triangle *)
+                      (firsttri + (samplenum * m->triangles.itemwords));
+      if (!deadtri(sampletri.tri)) {
+        org(sampletri, torg);
+        dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+               (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+        if (dist < searchdist) {
+          otricopy(sampletri, *searchtri);
+          searchdist = dist;
+          if (b->verbose > 2) {
+            printf("    Choosing triangle with origin (%.12g, %.12g).\n",
+                   torg[0], torg[1]);
+          }
+        }
+      }
+    }
+    sampleblock = (VOID **) *sampleblock;
+  }
+
+  /* Where are we? */
+  org(*searchtri, torg);
+  dest(*searchtri, tdest);
+  /* Check the starting triangle's vertices. */
+  if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
+    return ONVERTEX;
+  }
+  if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) {
+    lnextself(*searchtri);
+    return ONVERTEX;
+  }
+  /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */
+  ahead = counterclockwise(m, b, torg, tdest, searchpoint);
+  if (ahead < 0.0) {
+    /* Turn around so that `searchpoint' is to the left of the */
+    /*   edge specified by `searchtri'.                        */
+    symself(*searchtri);
+  } else if (ahead == 0.0) {
+    /* Check if `searchpoint' is between `torg' and `tdest'. */
+    if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) &&
+        ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) {
+      return ONEDGE;
+    }
+  }
+  return preciselocate(m, b, searchpoint, searchtri, 0);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Point location routines end here                          *********/
+
+/********* Mesh transformation routines begin here                   *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  insertsubseg()   Create a new subsegment and insert it between two       */
+/*                   triangles.                                              */
+/*                                                                           */
+/*  The new subsegment is inserted at the edge described by the handle       */
+/*  `tri'.  Its vertices are properly initialized.  The marker `subsegmark'  */
+/*  is applied to the subsegment and, if appropriate, its vertices.          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri,
+                  int subsegmark)
+#else /* not ANSI_DECLARATORS */
+void insertsubseg(m, b, tri, subsegmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *tri;             /* Edge at which to insert the new subsegment. */
+int subsegmark;                            /* Marker for the new subsegment. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri oppotri;
+  struct osub newsubseg;
+  vertex triorg, tridest;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*tri, triorg);
+  dest(*tri, tridest);
+  /* Mark vertices if possible. */
+  if (vertexmark(triorg) == 0) {
+    setvertexmark(triorg, subsegmark);
+  }
+  if (vertexmark(tridest) == 0) {
+    setvertexmark(tridest, subsegmark);
+  }
+  /* Check if there's already a subsegment here. */
+  tspivot(*tri, newsubseg);
+  if (newsubseg.ss == m->dummysub) {
+    /* Make new subsegment and initialize its vertices. */
+    makesubseg(m, &newsubseg);
+    setsorg(newsubseg, tridest);
+    setsdest(newsubseg, triorg);
+    /* Bond new subsegment to the two triangles it is sandwiched between. */
+    /*   Note that the facing triangle `oppotri' might be equal to        */
+    /*   `dummytri' (outer space), but the new subsegment is bonded to it */
+    /*   all the same.                                                    */
+    tsbond(*tri, newsubseg);
+    sym(*tri, oppotri);
+    ssymself(newsubseg);
+    tsbond(oppotri, newsubseg);
+    setmark(newsubseg, subsegmark);
+    if (b->verbose > 2) {
+      printf("  Inserting new ");
+      printsubseg(m, b, &newsubseg);
+    }
+  } else {
+    if (mark(newsubseg) == 0) {
+      setmark(newsubseg, subsegmark);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Terminology                                                              */
+/*                                                                           */
+/*  A "local transformation" replaces a small set of triangles with another  */
+/*  set of triangles.  This may or may not involve inserting or deleting a   */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*  The term "casing" is used to describe the set of triangles that are      */
+/*  attached to the triangles being transformed, but are not transformed     */
+/*  themselves.  Think of the casing as a fixed hollow structure inside      */
+/*  which all the action happens.  A "casing" is only defined relative to    */
+/*  a single transformation; each occurrence of a transformation will        */
+/*  involve a different casing.                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  flip()   Transform two triangles to two different triangles by flipping  */
+/*           an edge counterclockwise within a quadrilateral.                */
+/*                                                                           */
+/*  Imagine the original triangles, abc and bad, oriented so that the        */
+/*  shared edge ab lies in a horizontal plane, with the vertex b on the left */
+/*  and the vertex a on the right.  The vertex c lies below the edge, and    */
+/*  the vertex d lies above the edge.  The `flipedge' handle holds the edge  */
+/*  ab of triangle abc, and is directed left, from vertex a to vertex b.     */
+/*                                                                           */
+/*  The triangles abc and bad are deleted and replaced by the triangles cdb  */
+/*  and dca.  The triangles that represent abc and bad are NOT deallocated;  */
+/*  they are reused for dca and cdb, respectively.  Hence, any handles that  */
+/*  may have held the original triangles are still valid, although not       */
+/*  directed as they were before.                                            */
+/*                                                                           */
+/*  Upon completion of this routine, the `flipedge' handle holds the edge    */
+/*  dc of triangle dca, and is directed down, from vertex d to vertex c.     */
+/*  (Hence, the two triangles have rotated counterclockwise.)                */
+/*                                                                           */
+/*  WARNING:  This transformation is geometrically valid only if the         */
+/*  quadrilateral adbc is convex.  Furthermore, this transformation is       */
+/*  valid only if there is not a subsegment between the triangles abc and    */
+/*  bad.  This routine does not check either of these preconditions, and     */
+/*  it is the responsibility of the calling routine to ensure that they are  */
+/*  met.  If they are not, the streets shall be filled with wailing and      */
+/*  gnashing of teeth.                                                       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void flip(struct mesh *m, struct behavior *b, struct otri *flipedge)
+#else /* not ANSI_DECLARATORS */
+void flip(m, b, flipedge)
+struct mesh *m;
+struct behavior *b;
+struct otri *flipedge;                    /* Handle for the triangle abc. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri botleft, botright;
+  struct otri topleft, topright;
+  struct otri top;
+  struct otri botlcasing, botrcasing;
+  struct otri toplcasing, toprcasing;
+  struct osub botlsubseg, botrsubseg;
+  struct osub toplsubseg, toprsubseg;
+  vertex leftvertex, rightvertex, botvertex;
+  vertex farvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Identify the vertices of the quadrilateral. */
+  org(*flipedge, rightvertex);
+  dest(*flipedge, leftvertex);
+  apex(*flipedge, botvertex);
+  sym(*flipedge, top);
+#ifdef SELF_CHECK
+  if (top.tri == m->dummytri) {
+    printf("Internal error in flip():  Attempt to flip on boundary.\n");
+    lnextself(*flipedge);
+    return;
+  }
+  if (m->checksegments) {
+    tspivot(*flipedge, toplsubseg);
+    if (toplsubseg.ss != m->dummysub) {
+      printf("Internal error in flip():  Attempt to flip a segment.\n");
+      lnextself(*flipedge);
+      return;
+    }
+  }
+#endif /* SELF_CHECK */
+  apex(top, farvertex);
+
+  /* Identify the casing of the quadrilateral. */
+  lprev(top, topleft);
+  sym(topleft, toplcasing);
+  lnext(top, topright);
+  sym(topright, toprcasing);
+  lnext(*flipedge, botleft);
+  sym(botleft, botlcasing);
+  lprev(*flipedge, botright);
+  sym(botright, botrcasing);
+  /* Rotate the quadrilateral one-quarter turn counterclockwise. */
+  bond(topleft, botlcasing);
+  bond(botleft, botrcasing);
+  bond(botright, toprcasing);
+  bond(topright, toplcasing);
+
+  if (m->checksegments) {
+    /* Check for subsegments and rebond them to the quadrilateral. */
+    tspivot(topleft, toplsubseg);
+    tspivot(botleft, botlsubseg);
+    tspivot(botright, botrsubseg);
+    tspivot(topright, toprsubseg);
+    if (toplsubseg.ss == m->dummysub) {
+      tsdissolve(topright);
+    } else {
+      tsbond(topright, toplsubseg);
+    }
+    if (botlsubseg.ss == m->dummysub) {
+      tsdissolve(topleft);
+    } else {
+      tsbond(topleft, botlsubseg);
+    }
+    if (botrsubseg.ss == m->dummysub) {
+      tsdissolve(botleft);
+    } else {
+      tsbond(botleft, botrsubseg);
+    }
+    if (toprsubseg.ss == m->dummysub) {
+      tsdissolve(botright);
+    } else {
+      tsbond(botright, toprsubseg);
+    }
+  }
+
+  /* New vertex assignments for the rotated quadrilateral. */
+  setorg(*flipedge, farvertex);
+  setdest(*flipedge, botvertex);
+  setapex(*flipedge, rightvertex);
+  setorg(top, botvertex);
+  setdest(top, farvertex);
+  setapex(top, leftvertex);
+  if (b->verbose > 2) {
+    printf("  Edge flip results in left ");
+    printtriangle(m, b, &top);
+    printf("  and right ");
+    printtriangle(m, b, flipedge);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  unflip()   Transform two triangles to two different triangles by         */
+/*             flipping an edge clockwise within a quadrilateral.  Reverses  */
+/*             the flip() operation so that the data structures representing */
+/*             the triangles are back where they were before the flip().     */
+/*                                                                           */
+/*  Imagine the original triangles, abc and bad, oriented so that the        */
+/*  shared edge ab lies in a horizontal plane, with the vertex b on the left */
+/*  and the vertex a on the right.  The vertex c lies below the edge, and    */
+/*  the vertex d lies above the edge.  The `flipedge' handle holds the edge  */
+/*  ab of triangle abc, and is directed left, from vertex a to vertex b.     */
+/*                                                                           */
+/*  The triangles abc and bad are deleted and replaced by the triangles cdb  */
+/*  and dca.  The triangles that represent abc and bad are NOT deallocated;  */
+/*  they are reused for cdb and dca, respectively.  Hence, any handles that  */
+/*  may have held the original triangles are still valid, although not       */
+/*  directed as they were before.                                            */
+/*                                                                           */
+/*  Upon completion of this routine, the `flipedge' handle holds the edge    */
+/*  cd of triangle cdb, and is directed up, from vertex c to vertex d.       */
+/*  (Hence, the two triangles have rotated clockwise.)                       */
+/*                                                                           */
+/*  WARNING:  This transformation is geometrically valid only if the         */
+/*  quadrilateral adbc is convex.  Furthermore, this transformation is       */
+/*  valid only if there is not a subsegment between the triangles abc and    */
+/*  bad.  This routine does not check either of these preconditions, and     */
+/*  it is the responsibility of the calling routine to ensure that they are  */
+/*  met.  If they are not, the streets shall be filled with wailing and      */
+/*  gnashing of teeth.                                                       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge)
+#else /* not ANSI_DECLARATORS */
+void unflip(m, b, flipedge)
+struct mesh *m;
+struct behavior *b;
+struct otri *flipedge;                    /* Handle for the triangle abc. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri botleft, botright;
+  struct otri topleft, topright;
+  struct otri top;
+  struct otri botlcasing, botrcasing;
+  struct otri toplcasing, toprcasing;
+  struct osub botlsubseg, botrsubseg;
+  struct osub toplsubseg, toprsubseg;
+  vertex leftvertex, rightvertex, botvertex;
+  vertex farvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Identify the vertices of the quadrilateral. */
+  org(*flipedge, rightvertex);
+  dest(*flipedge, leftvertex);
+  apex(*flipedge, botvertex);
+  sym(*flipedge, top);
+#ifdef SELF_CHECK
+  if (top.tri == m->dummytri) {
+    printf("Internal error in unflip():  Attempt to flip on boundary.\n");
+    lnextself(*flipedge);
+    return;
+  }
+  if (m->checksegments) {
+    tspivot(*flipedge, toplsubseg);
+    if (toplsubseg.ss != m->dummysub) {
+      printf("Internal error in unflip():  Attempt to flip a subsegment.\n");
+      lnextself(*flipedge);
+      return;
+    }
+  }
+#endif /* SELF_CHECK */
+  apex(top, farvertex);
+
+  /* Identify the casing of the quadrilateral. */
+  lprev(top, topleft);
+  sym(topleft, toplcasing);
+  lnext(top, topright);
+  sym(topright, toprcasing);
+  lnext(*flipedge, botleft);
+  sym(botleft, botlcasing);
+  lprev(*flipedge, botright);
+  sym(botright, botrcasing);
+  /* Rotate the quadrilateral one-quarter turn clockwise. */
+  bond(topleft, toprcasing);
+  bond(botleft, toplcasing);
+  bond(botright, botlcasing);
+  bond(topright, botrcasing);
+
+  if (m->checksegments) {
+    /* Check for subsegments and rebond them to the quadrilateral. */
+    tspivot(topleft, toplsubseg);
+    tspivot(botleft, botlsubseg);
+    tspivot(botright, botrsubseg);
+    tspivot(topright, toprsubseg);
+    if (toplsubseg.ss == m->dummysub) {
+      tsdissolve(botleft);
+    } else {
+      tsbond(botleft, toplsubseg);
+    }
+    if (botlsubseg.ss == m->dummysub) {
+      tsdissolve(botright);
+    } else {
+      tsbond(botright, botlsubseg);
+    }
+    if (botrsubseg.ss == m->dummysub) {
+      tsdissolve(topright);
+    } else {
+      tsbond(topright, botrsubseg);
+    }
+    if (toprsubseg.ss == m->dummysub) {
+      tsdissolve(topleft);
+    } else {
+      tsbond(topleft, toprsubseg);
+    }
+  }
+
+  /* New vertex assignments for the rotated quadrilateral. */
+  setorg(*flipedge, botvertex);
+  setdest(*flipedge, farvertex);
+  setapex(*flipedge, leftvertex);
+  setorg(top, farvertex);
+  setdest(top, botvertex);
+  setapex(top, rightvertex);
+  if (b->verbose > 2) {
+    printf("  Edge unflip results in left ");
+    printtriangle(m, b, flipedge);
+    printf("  and right ");
+    printtriangle(m, b, &top);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  insertvertex()   Insert a vertex into a Delaunay triangulation,          */
+/*                   performing flips as necessary to maintain the Delaunay  */
+/*                   property.                                               */
+/*                                                                           */
+/*  The point `insertvertex' is located.  If `searchtri.tri' is not NULL,    */
+/*  the search for the containing triangle begins from `searchtri'.  If      */
+/*  `searchtri.tri' is NULL, a full point location procedure is called.      */
+/*  If `insertvertex' is found inside a triangle, the triangle is split into */
+/*  three; if `insertvertex' lies on an edge, the edge is split in two,      */
+/*  thereby splitting the two adjacent triangles into four.  Edge flips are  */
+/*  used to restore the Delaunay property.  If `insertvertex' lies on an     */
+/*  existing vertex, no action is taken, and the value DUPLICATEVERTEX is    */
+/*  returned.  On return, `searchtri' is set to a handle whose origin is the */
+/*  existing vertex.                                                         */
+/*                                                                           */
+/*  Normally, the parameter `splitseg' is set to NULL, implying that no      */
+/*  subsegment should be split.  In this case, if `insertvertex' is found to */
+/*  lie on a segment, no action is taken, and the value VIOLATINGVERTEX is   */
+/*  returned.  On return, `searchtri' is set to a handle whose primary edge  */
+/*  is the violated subsegment.                                              */
+/*                                                                           */
+/*  If the calling routine wishes to split a subsegment by inserting a       */
+/*  vertex in it, the parameter `splitseg' should be that subsegment.  In    */
+/*  this case, `searchtri' MUST be the triangle handle reached by pivoting   */
+/*  from that subsegment; no point location is done.                         */
+/*                                                                           */
+/*  `segmentflaws' and `triflaws' are flags that indicate whether or not     */
+/*  there should be checks for the creation of encroached subsegments or bad */
+/*  quality triangles.  If a newly inserted vertex encroaches upon           */
+/*  subsegments, these subsegments are added to the list of subsegments to   */
+/*  be split if `segmentflaws' is set.  If bad triangles are created, these  */
+/*  are added to the queue if `triflaws' is set.                             */
+/*                                                                           */
+/*  If a duplicate vertex or violated segment does not prevent the vertex    */
+/*  from being inserted, the return value will be ENCROACHINGVERTEX if the   */
+/*  vertex encroaches upon a subsegment (and checking is enabled), or        */
+/*  SUCCESSFULVERTEX otherwise.  In either case, `searchtri' is set to a     */
+/*  handle whose origin is the newly inserted vertex.                        */
+/*                                                                           */
+/*  insertvertex() does not use flip() for reasons of speed; some            */
+/*  information can be reused from edge flip to edge flip, like the          */
+/*  locations of subsegments.                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b,
+                                     vertex newvertex, struct otri *searchtri,
+                                     struct osub *splitseg,
+                                     int segmentflaws, int triflaws,
+                                     REAL iradius)
+#else /* not ANSI_DECLARATORS */
+enum insertvertexresult insertvertex(m, b, newvertex, searchtri, splitseg,
+                                     segmentflaws, triflaws, iradius)
+struct mesh *m;
+struct behavior *b;
+vertex newvertex;
+struct otri *searchtri;
+struct osub *splitseg;
+int segmentflaws;
+int triflaws;
+REAL iradius;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri horiz;
+  struct otri top;
+  struct otri botleft, botright;
+  struct otri topleft, topright;
+  struct otri newbotleft, newbotright;
+  struct otri newtopright;
+  struct otri botlcasing, botrcasing;
+  struct otri toplcasing, toprcasing;
+  struct otri testtri;
+  struct osub botlsubseg, botrsubseg;
+  struct osub toplsubseg, toprsubseg;
+  struct osub brokensubseg;
+  struct osub checksubseg;
+  struct osub rightsubseg;
+  struct osub newsubseg;
+  struct badsubseg *encroached;
+  struct flipstacker *newflip;
+  vertex first;
+  vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
+  REAL attrib;
+  REAL area;
+  enum insertvertexresult success;
+  enum locateresult intersect;
+  int doflip;
+  int mirrorflag;
+  int enq;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;         /* Temporary variable used by spivot() and tspivot(). */
+
+  if (b->verbose > 1) {
+    printf("  Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+  }
+
+  if (splitseg == (struct osub *) NULL) {
+    /* Find the location of the vertex to be inserted.  Check if a good */
+    /*   starting triangle has already been provided by the caller.     */
+    if (searchtri->tri == m->dummytri) {
+      /* Find a boundary triangle. */
+      horiz.tri = m->dummytri;
+      horiz.orient = 0;
+      symself(horiz);
+      /* Search for a triangle containing `newvertex'. */
+      intersect = locate(m, b, newvertex, &horiz);
+    } else {
+      /* Start searching from the triangle provided by the caller. */
+      otricopy(*searchtri, horiz);
+      intersect = preciselocate(m, b, newvertex, &horiz, 1);
+    }
+  } else {
+    /* The calling routine provides the subsegment in which */
+    /*   the vertex is inserted.                             */
+    otricopy(*searchtri, horiz);
+    intersect = ONEDGE;
+  }
+  if (intersect == ONVERTEX) {
+    /* There's already a vertex there.  Return in `searchtri' a triangle */
+    /*   whose origin is the existing vertex.                            */
+    otricopy(horiz, *searchtri);
+    otricopy(horiz, m->recenttri);
+    return DUPLICATEVERTEX;
+  }
+  if ((intersect == ONEDGE) || (intersect == OUTSIDE)) {
+    /* The vertex falls on an edge or boundary. */
+    if (m->checksegments && (splitseg == (struct osub *) NULL)) {
+      /* Check whether the vertex falls on a subsegment. */
+      tspivot(horiz, brokensubseg);
+      if (brokensubseg.ss != m->dummysub) {
+        /* The vertex falls on a subsegment, and hence will not be inserted. */
+        if (segmentflaws) {
+          if (b->nobisect == 2) {
+            enq = 0;
+#ifndef CDT_ONLY
+          } else if (iradius > 0.0) {
+            enq = splitpermitted(m, &brokensubseg, iradius);
+#endif /* not CDT_ONLY */
+          } else {
+            enq = 1;
+          }
+          if (enq && (b->nobisect == 1)) {
+            /* This subsegment may be split only if it is an */
+            /*   internal boundary.                          */
+            sym(horiz, testtri);
+            enq = testtri.tri != m->dummytri;
+          }
+          if (enq) {
+            /* Add the subsegment to the list of encroached subsegments. */
+            encroached = (struct badsubseg *) poolalloc(&m->badsubsegs);
+            encroached->encsubseg = sencode(brokensubseg);
+            sorg(brokensubseg, encroached->subsegorg);
+            sdest(brokensubseg, encroached->subsegdest);
+            if (b->verbose > 2) {
+              printf(
+          "  Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
+                     encroached->subsegorg[0], encroached->subsegorg[1],
+                     encroached->subsegdest[0], encroached->subsegdest[1]);
+            }
+          }
+        }
+        /* Return a handle whose primary edge contains the vertex, */
+        /*   which has not been inserted.                          */
+        otricopy(horiz, *searchtri);
+        otricopy(horiz, m->recenttri);
+        return VIOLATINGVERTEX;
+      }
+    }
+
+    /* Insert the vertex on an edge, dividing one triangle into two (if */
+    /*   the edge lies on a boundary) or two triangles into four.       */
+    lprev(horiz, botright);
+    sym(botright, botrcasing);
+    sym(horiz, topright);
+    /* Is there a second triangle?  (Or does this edge lie on a boundary?) */
+    mirrorflag = topright.tri != m->dummytri;
+    if (mirrorflag) {
+      lnextself(topright);
+      sym(topright, toprcasing);
+      maketriangle(m, b, &newtopright);
+    } else {
+      /* Splitting a boundary edge increases the number of boundary edges. */
+      m->hullsize++;
+    }
+    maketriangle(m, b, &newbotright);
+
+    /* Set the vertices of changed and new triangles. */
+    org(horiz, rightvertex);
+    dest(horiz, leftvertex);
+    apex(horiz, botvertex);
+    setorg(newbotright, botvertex);
+    setdest(newbotright, rightvertex);
+    setapex(newbotright, newvertex);
+    setorg(horiz, newvertex);
+    for (i = 0; i < m->eextras; i++) {
+      /* Set the element attributes of a new triangle. */
+      setelemattribute(newbotright, i, elemattribute(botright, i));
+    }
+    if (b->vararea) {
+      /* Set the area constraint of a new triangle. */
+      setareabound(newbotright, areabound(botright));
+    }
+    if (mirrorflag) {
+      dest(topright, topvertex);
+      setorg(newtopright, rightvertex);
+      setdest(newtopright, topvertex);
+      setapex(newtopright, newvertex);
+      setorg(topright, newvertex);
+      for (i = 0; i < m->eextras; i++) {
+        /* Set the element attributes of another new triangle. */
+        setelemattribute(newtopright, i, elemattribute(topright, i));
+      }
+      if (b->vararea) {
+        /* Set the area constraint of another new triangle. */
+        setareabound(newtopright, areabound(topright));
+      }
+    }
+
+    /* There may be subsegments that need to be bonded */
+    /*   to the new triangle(s).                       */
+    if (m->checksegments) {
+      tspivot(botright, botrsubseg);
+      if (botrsubseg.ss != m->dummysub) {
+        tsdissolve(botright);
+        tsbond(newbotright, botrsubseg);
+      }
+      if (mirrorflag) {
+        tspivot(topright, toprsubseg);
+        if (toprsubseg.ss != m->dummysub) {
+          tsdissolve(topright);
+          tsbond(newtopright, toprsubseg);
+        }
+      }
+    }
+
+    /* Bond the new triangle(s) to the surrounding triangles. */
+    bond(newbotright, botrcasing);
+    lprevself(newbotright);
+    bond(newbotright, botright);
+    lprevself(newbotright);
+    if (mirrorflag) {
+      bond(newtopright, toprcasing);
+      lnextself(newtopright);
+      bond(newtopright, topright);
+      lnextself(newtopright);
+      bond(newtopright, newbotright);
+    }
+
+    if (splitseg != (struct osub *) NULL) {
+      /* Split the subsegment into two. */
+      setsdest(*splitseg, newvertex);
+      ssymself(*splitseg);
+      spivot(*splitseg, rightsubseg);
+      insertsubseg(m, b, &newbotright, mark(*splitseg));
+      tspivot(newbotright, newsubseg);
+      sbond(*splitseg, newsubseg);
+      ssymself(newsubseg);
+      sbond(newsubseg, rightsubseg);
+      ssymself(*splitseg);
+      /* Transfer the subsegment's boundary marker to the vertex */
+      /*   if required.                                          */
+      if (vertexmark(newvertex) == 0) {
+        setvertexmark(newvertex, mark(*splitseg));
+      }
+    }
+
+    if (m->checkquality) {
+      poolrestart(&m->flipstackers);
+      m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+      m->lastflip->flippedtri = encode(horiz);
+      m->lastflip->prevflip = (struct flipstacker *) &insertvertex;
+    }
+
+#ifdef SELF_CHECK
+    if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf(
+            "  Clockwise triangle prior to edge vertex insertion (bottom).\n");
+    }
+    if (mirrorflag) {
+      if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) {
+        printf("Internal error in insertvertex():\n");
+        printf("  Clockwise triangle prior to edge vertex insertion (top).\n");
+      }
+      if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) {
+        printf("Internal error in insertvertex():\n");
+        printf(
+            "  Clockwise triangle after edge vertex insertion (top right).\n");
+      }
+      if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) {
+        printf("Internal error in insertvertex():\n");
+        printf(
+            "  Clockwise triangle after edge vertex insertion (top left).\n");
+      }
+    }
+    if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf(
+          "  Clockwise triangle after edge vertex insertion (bottom left).\n");
+    }
+    if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf(
+        "  Clockwise triangle after edge vertex insertion (bottom right).\n");
+    }
+#endif /* SELF_CHECK */
+    if (b->verbose > 2) {
+      printf("  Updating bottom left ");
+      printtriangle(m, b, &botright);
+      if (mirrorflag) {
+        printf("  Updating top left ");
+        printtriangle(m, b, &topright);
+        printf("  Creating top right ");
+        printtriangle(m, b, &newtopright);
+      }
+      printf("  Creating bottom right ");
+      printtriangle(m, b, &newbotright);
+    }
+
+    /* Position `horiz' on the first edge to check for */
+    /*   the Delaunay property.                        */
+    lnextself(horiz);
+  } else {
+    /* Insert the vertex in a triangle, splitting it into three. */
+    lnext(horiz, botleft);
+    lprev(horiz, botright);
+    sym(botleft, botlcasing);
+    sym(botright, botrcasing);
+    maketriangle(m, b, &newbotleft);
+    maketriangle(m, b, &newbotright);
+
+    /* Set the vertices of changed and new triangles. */
+    org(horiz, rightvertex);
+    dest(horiz, leftvertex);
+    apex(horiz, botvertex);
+    setorg(newbotleft, leftvertex);
+    setdest(newbotleft, botvertex);
+    setapex(newbotleft, newvertex);
+    setorg(newbotright, botvertex);
+    setdest(newbotright, rightvertex);
+    setapex(newbotright, newvertex);
+    setapex(horiz, newvertex);
+    for (i = 0; i < m->eextras; i++) {
+      /* Set the element attributes of the new triangles. */
+      attrib = elemattribute(horiz, i);
+      setelemattribute(newbotleft, i, attrib);
+      setelemattribute(newbotright, i, attrib);
+    }
+    if (b->vararea) {
+      /* Set the area constraint of the new triangles. */
+      area = areabound(horiz);
+      setareabound(newbotleft, area);
+      setareabound(newbotright, area);
+    }
+
+    /* There may be subsegments that need to be bonded */
+    /*   to the new triangles.                         */
+    if (m->checksegments) {
+      tspivot(botleft, botlsubseg);
+      if (botlsubseg.ss != m->dummysub) {
+        tsdissolve(botleft);
+        tsbond(newbotleft, botlsubseg);
+      }
+      tspivot(botright, botrsubseg);
+      if (botrsubseg.ss != m->dummysub) {
+        tsdissolve(botright);
+        tsbond(newbotright, botrsubseg);
+      }
+    }
+
+    /* Bond the new triangles to the surrounding triangles. */
+    bond(newbotleft, botlcasing);
+    bond(newbotright, botrcasing);
+    lnextself(newbotleft);
+    lprevself(newbotright);
+    bond(newbotleft, newbotright);
+    lnextself(newbotleft);
+    bond(botleft, newbotleft);
+    lprevself(newbotright);
+    bond(botright, newbotright);
+
+    if (m->checkquality) {
+      poolrestart(&m->flipstackers);
+      m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+      m->lastflip->flippedtri = encode(horiz);
+      m->lastflip->prevflip = (struct flipstacker *) NULL;
+    }
+
+#ifdef SELF_CHECK
+    if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle prior to vertex insertion.\n");
+    }
+    if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle after vertex insertion (top).\n");
+    }
+    if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle after vertex insertion (left).\n");
+    }
+    if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle after vertex insertion (right).\n");
+    }
+#endif /* SELF_CHECK */
+    if (b->verbose > 2) {
+      printf("  Updating top ");
+      printtriangle(m, b, &horiz);
+      printf("  Creating left ");
+      printtriangle(m, b, &newbotleft);
+      printf("  Creating right ");
+      printtriangle(m, b, &newbotright);
+    }
+  }
+
+  /* The insertion is successful by default, unless an encroached */
+  /*   subsegment is found.                                       */
+  success = SUCCESSFULVERTEX;
+  /* Circle around the newly inserted vertex, checking each edge opposite */
+  /*   it for the Delaunay property.  Non-Delaunay edges are flipped.     */
+  /*   `horiz' is always the edge being checked.  `first' marks where to  */
+  /*   stop circling.                                                     */
+  org(horiz, first);
+  rightvertex = first;
+  dest(horiz, leftvertex);
+  /* Circle until finished. */
+  while (1) {
+    /* By default, the edge will be flipped. */
+    doflip = 1;
+
+    if (m->checksegments) {
+      /* Check for a subsegment, which cannot be flipped. */
+      tspivot(horiz, checksubseg);
+      if (checksubseg.ss != m->dummysub) {
+        /* The edge is a subsegment and cannot be flipped. */
+        doflip = 0;
+#ifndef CDT_ONLY
+        if (segmentflaws) {
+          /* Does the new vertex encroach upon this subsegment? */
+          if (checkseg4encroach(m, b, &checksubseg, iradius)) {
+            success = ENCROACHINGVERTEX;
+          }
+        }
+#endif /* not CDT_ONLY */
+      }
+    }
+
+    if (doflip) {
+      /* Check if the edge is a boundary edge. */
+      sym(horiz, top);
+      if (top.tri == m->dummytri) {
+        /* The edge is a boundary edge and cannot be flipped. */
+        doflip = 0;
+      } else {
+        /* Find the vertex on the other side of the edge. */
+        apex(top, farvertex);
+        /* In the incremental Delaunay triangulation algorithm, any of      */
+        /*   `leftvertex', `rightvertex', and `farvertex' could be vertices */
+        /*   of the triangular bounding box.  These vertices must be        */
+        /*   treated as if they are infinitely distant, even though their   */
+        /*   "coordinates" are not.                                         */
+        if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) ||
+            (leftvertex == m->infvertex3)) {
+          /* `leftvertex' is infinitely distant.  Check the convexity of  */
+          /*   the boundary of the triangulation.  'farvertex' might be   */
+          /*   infinite as well, but trust me, this same condition should */
+          /*   be applied.                                                */
+          doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex)
+                   > 0.0;
+        } else if ((rightvertex == m->infvertex1) ||
+                   (rightvertex == m->infvertex2) ||
+                   (rightvertex == m->infvertex3)) {
+          /* `rightvertex' is infinitely distant.  Check the convexity of */
+          /*   the boundary of the triangulation.  'farvertex' might be   */
+          /*   infinite as well, but trust me, this same condition should */
+          /*   be applied.                                                */
+          doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex)
+                   > 0.0;
+        } else if ((farvertex == m->infvertex1) ||
+                   (farvertex == m->infvertex2) ||
+                   (farvertex == m->infvertex3)) {
+          /* `farvertex' is infinitely distant and cannot be inside */
+          /*   the circumcircle of the triangle `horiz'.            */
+          doflip = 0;
+        } else {
+          /* Test whether the edge is locally Delaunay. */
+          doflip = incircle(m, b, leftvertex, newvertex, rightvertex,
+                            farvertex) > 0.0;
+        }
+        if (doflip) {
+          /* We made it!  Flip the edge `horiz' by rotating its containing */
+          /*   quadrilateral (the two triangles adjacent to `horiz').      */
+          /* Identify the casing of the quadrilateral. */
+          lprev(top, topleft);
+          sym(topleft, toplcasing);
+          lnext(top, topright);
+          sym(topright, toprcasing);
+          lnext(horiz, botleft);
+          sym(botleft, botlcasing);
+          lprev(horiz, botright);
+          sym(botright, botrcasing);
+          /* Rotate the quadrilateral one-quarter turn counterclockwise. */
+          bond(topleft, botlcasing);
+          bond(botleft, botrcasing);
+          bond(botright, toprcasing);
+          bond(topright, toplcasing);
+          if (m->checksegments) {
+            /* Check for subsegments and rebond them to the quadrilateral. */
+            tspivot(topleft, toplsubseg);
+            tspivot(botleft, botlsubseg);
+            tspivot(botright, botrsubseg);
+            tspivot(topright, toprsubseg);
+            if (toplsubseg.ss == m->dummysub) {
+              tsdissolve(topright);
+            } else {
+              tsbond(topright, toplsubseg);
+            }
+            if (botlsubseg.ss == m->dummysub) {
+              tsdissolve(topleft);
+            } else {
+              tsbond(topleft, botlsubseg);
+            }
+            if (botrsubseg.ss == m->dummysub) {
+              tsdissolve(botleft);
+            } else {
+              tsbond(botleft, botrsubseg);
+            }
+            if (toprsubseg.ss == m->dummysub) {
+              tsdissolve(botright);
+            } else {
+              tsbond(botright, toprsubseg);
+            }
+          }
+          /* New vertex assignments for the rotated quadrilateral. */
+          setorg(horiz, farvertex);
+          setdest(horiz, newvertex);
+          setapex(horiz, rightvertex);
+          setorg(top, newvertex);
+          setdest(top, farvertex);
+          setapex(top, leftvertex);
+          for (i = 0; i < m->eextras; i++) {
+            /* Take the average of the two triangles' attributes. */
+            attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i));
+            setelemattribute(top, i, attrib);
+            setelemattribute(horiz, i, attrib);
+          }
+          if (b->vararea) {
+            if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) {
+              area = -1.0;
+            } else {
+              /* Take the average of the two triangles' area constraints.    */
+              /*   This prevents small area constraints from migrating a     */
+              /*   long, long way from their original location due to flips. */
+              area = 0.5 * (areabound(top) + areabound(horiz));
+            }
+            setareabound(top, area);
+            setareabound(horiz, area);
+          }
+
+          if (m->checkquality) {
+            newflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+            newflip->flippedtri = encode(horiz);
+            newflip->prevflip = m->lastflip;
+            m->lastflip = newflip;
+          }
+
+#ifdef SELF_CHECK
+          if (newvertex != (vertex) NULL) {
+            if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle prior to edge flip (bottom).\n");
+            }
+            /* The following test has been removed because constrainededge() */
+            /*   sometimes generates inverted triangles that insertvertex()  */
+            /*   removes.                                                    */
+/*
+            if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle prior to edge flip (top).\n");
+            }
+*/
+            if (counterclockwise(m, b, farvertex, leftvertex, newvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle after edge flip (left).\n");
+            }
+            if (counterclockwise(m, b, newvertex, rightvertex, farvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle after edge flip (right).\n");
+            }
+          }
+#endif /* SELF_CHECK */
+          if (b->verbose > 2) {
+            printf("  Edge flip results in left ");
+            lnextself(topleft);
+            printtriangle(m, b, &topleft);
+            printf("  and right ");
+            printtriangle(m, b, &horiz);
+          }
+          /* On the next iterations, consider the two edges that were  */
+          /*   exposed (this is, are now visible to the newly inserted */
+          /*   vertex) by the edge flip.                               */
+          lprevself(horiz);
+          leftvertex = farvertex;
+        }
+      }
+    }
+    if (!doflip) {
+      /* The handle `horiz' is accepted as locally Delaunay. */
+#ifndef CDT_ONLY
+      if (triflaws) {
+        /* Check the triangle `horiz' for quality. */
+        testtriangle(m, b, &horiz);
+      }
+#endif /* not CDT_ONLY */
+      /* Look for the next edge around the newly inserted vertex. */
+      lnextself(horiz);
+      sym(horiz, testtri);
+      /* Check for finishing a complete revolution about the new vertex, or */
+      /*   falling outside  of the triangulation.  The latter will happen   */
+      /*   when a vertex is inserted at a boundary.                         */
+      if ((leftvertex == first) || (testtri.tri == m->dummytri)) {
+        /* We're done.  Return a triangle whose origin is the new vertex. */
+        lnext(horiz, *searchtri);
+        lnext(horiz, m->recenttri);
+        return success;
+      }
+      /* Finish finding the next edge around the newly inserted vertex. */
+      lnext(testtri, horiz);
+      rightvertex = leftvertex;
+      dest(horiz, leftvertex);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangulatepolygon()   Find the Delaunay triangulation of a polygon that */
+/*                         has a certain "nice" shape.  This includes the    */
+/*                         polygons that result from deletion of a vertex or */
+/*                         insertion of a segment.                           */
+/*                                                                           */
+/*  This is a conceptually difficult routine.  The starting assumption is    */
+/*  that we have a polygon with n sides.  n - 1 of these sides are currently */
+/*  represented as edges in the mesh.  One side, called the "base", need not */
+/*  be.                                                                      */
+/*                                                                           */
+/*  Inside the polygon is a structure I call a "fan", consisting of n - 1    */
+/*  triangles that share a common origin.  For each of these triangles, the  */
+/*  edge opposite the origin is one of the sides of the polygon.  The        */
+/*  primary edge of each triangle is the edge directed from the origin to    */
+/*  the destination; note that this is not the same edge that is a side of   */
+/*  the polygon.  `firstedge' is the primary edge of the first triangle.     */
+/*  From there, the triangles follow in counterclockwise order about the     */
+/*  polygon, until `lastedge', the primary edge of the last triangle.        */
+/*  `firstedge' and `lastedge' are probably connected to other triangles     */
+/*  beyond the extremes of the fan, but their identity is not important, as  */
+/*  long as the fan remains connected to them.                               */
+/*                                                                           */
+/*  Imagine the polygon oriented so that its base is at the bottom.  This    */
+/*  puts `firstedge' on the far right, and `lastedge' on the far left.       */
+/*  The right vertex of the base is the destination of `firstedge', and the  */
+/*  left vertex of the base is the apex of `lastedge'.                       */
+/*                                                                           */
+/*  The challenge now is to find the right sequence of edge flips to         */
+/*  transform the fan into a Delaunay triangulation of the polygon.  Each    */
+/*  edge flip effectively removes one triangle from the fan, committing it   */
+/*  to the polygon.  The resulting polygon has one fewer edge.  If `doflip'  */
+/*  is set, the final flip will be performed, resulting in a fan of one      */
+/*  (useless?) triangle.  If `doflip' is not set, the final flip is not      */
+/*  performed, resulting in a fan of two triangles, and an unfinished        */
+/*  triangular polygon that is not yet filled out with a single triangle.    */
+/*  On completion of the routine, `lastedge' is the last remaining triangle, */
+/*  or the leftmost of the last two.                                         */
+/*                                                                           */
+/*  Although the flips are performed in the order described above, the       */
+/*  decisions about what flips to perform are made in precisely the reverse  */
+/*  order.  The recursive triangulatepolygon() procedure makes a decision,   */
+/*  uses up to two recursive calls to triangulate the "subproblems"          */
+/*  (polygons with fewer edges), and then performs an edge flip.             */
+/*                                                                           */
+/*  The "decision" it makes is which vertex of the polygon should be         */
+/*  connected to the base.  This decision is made by testing every possible  */
+/*  vertex.  Once the best vertex is found, the two edges that connect this  */
+/*  vertex to the base become the bases for two smaller polygons.  These     */
+/*  are triangulated recursively.  Unfortunately, this approach can take     */
+/*  O(n^2) time not only in the worst case, but in many common cases.  It's  */
+/*  rarely a big deal for vertex deletion, where n is rarely larger than     */
+/*  ten, but it could be a big deal for segment insertion, especially if     */
+/*  there's a lot of long segments that each cut many triangles.  I ought to */
+/*  code a faster algorithm some day.                                        */
+/*                                                                           */
+/*  The `edgecount' parameter is the number of sides of the polygon,         */
+/*  including its base.  `triflaws' is a flag that determines whether the    */
+/*  new triangles should be tested for quality, and enqueued if they are     */
+/*  bad.                                                                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangulatepolygon(struct mesh *m, struct behavior *b,
+                        struct otri *firstedge, struct otri *lastedge,
+                        int edgecount, int doflip, int triflaws)
+#else /* not ANSI_DECLARATORS */
+void triangulatepolygon(m, b, firstedge, lastedge, edgecount, doflip, triflaws)
+struct mesh *m;
+struct behavior *b;
+struct otri *firstedge;
+struct otri *lastedge;
+int edgecount;
+int doflip;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri testtri;
+  struct otri besttri;
+  struct otri tempedge;
+  vertex leftbasevertex, rightbasevertex;
+  vertex testvertex;
+  vertex bestvertex;
+  int bestnumber;
+  int i;
+  triangle ptr;   /* Temporary variable used by sym(), onext(), and oprev(). */
+
+  /* Identify the base vertices. */
+  apex(*lastedge, leftbasevertex);
+  dest(*firstedge, rightbasevertex);
+  if (b->verbose > 2) {
+    printf("  Triangulating interior polygon at edge\n");
+    printf("    (%.12g, %.12g) (%.12g, %.12g)\n", leftbasevertex[0],
+           leftbasevertex[1], rightbasevertex[0], rightbasevertex[1]);
+  }
+  /* Find the best vertex to connect the base to. */
+  onext(*firstedge, besttri);
+  dest(besttri, bestvertex);
+  otricopy(besttri, testtri);
+  bestnumber = 1;
+  for (i = 2; i <= edgecount - 2; i++) {
+    onextself(testtri);
+    dest(testtri, testvertex);
+    /* Is this a better vertex? */
+    if (incircle(m, b, leftbasevertex, rightbasevertex, bestvertex,
+                 testvertex) > 0.0) {
+      otricopy(testtri, besttri);
+      bestvertex = testvertex;
+      bestnumber = i;
+    }
+  }
+  if (b->verbose > 2) {
+    printf("    Connecting edge to (%.12g, %.12g)\n", bestvertex[0],
+           bestvertex[1]);
+  }
+  if (bestnumber > 1) {
+    /* Recursively triangulate the smaller polygon on the right. */
+    oprev(besttri, tempedge);
+    triangulatepolygon(m, b, firstedge, &tempedge, bestnumber + 1, 1,
+                       triflaws);
+  }
+  if (bestnumber < edgecount - 2) {
+    /* Recursively triangulate the smaller polygon on the left. */
+    sym(besttri, tempedge);
+    triangulatepolygon(m, b, &besttri, lastedge, edgecount - bestnumber, 1,
+                       triflaws);
+    /* Find `besttri' again; it may have been lost to edge flips. */
+    sym(tempedge, besttri);
+  }
+  if (doflip) {
+    /* Do one final edge flip. */
+    flip(m, b, &besttri);
+#ifndef CDT_ONLY
+    if (triflaws) {
+      /* Check the quality of the newly committed triangle. */
+      sym(besttri, testtri);
+      testtriangle(m, b, &testtri);
+    }
+#endif /* not CDT_ONLY */
+  }
+  /* Return the base triangle. */
+  otricopy(besttri, *lastedge);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  deletevertex()   Delete a vertex from a Delaunay triangulation, ensuring */
+/*                   that the triangulation remains Delaunay.                */
+/*                                                                           */
+/*  The origin of `deltri' is deleted.  The union of the triangles adjacent  */
+/*  to this vertex is a polygon, for which the Delaunay triangulation is     */
+/*  found.  Two triangles are removed from the mesh.                         */
+/*                                                                           */
+/*  Only interior vertices that do not lie on segments or boundaries may be  */
+/*  deleted.                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void deletevertex(struct mesh *m, struct behavior *b, struct otri *deltri)
+#else /* not ANSI_DECLARATORS */
+void deletevertex(m, b, deltri)
+struct mesh *m;
+struct behavior *b;
+struct otri *deltri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri countingtri;
+  struct otri firstedge, lastedge;
+  struct otri deltriright;
+  struct otri lefttri, righttri;
+  struct otri leftcasing, rightcasing;
+  struct osub leftsubseg, rightsubseg;
+  vertex delvertex;
+  vertex neworg;
+  int edgecount;
+  triangle ptr;   /* Temporary variable used by sym(), onext(), and oprev(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*deltri, delvertex);
+  if (b->verbose > 1) {
+    printf("  Deleting (%.12g, %.12g).\n", delvertex[0], delvertex[1]);
+  }
+  vertexdealloc(m, delvertex);
+
+  /* Count the degree of the vertex being deleted. */
+  onext(*deltri, countingtri);
+  edgecount = 1;
+  while (!otriequal(*deltri, countingtri)) {
+#ifdef SELF_CHECK
+    if (countingtri.tri == m->dummytri) {
+      printf("Internal error in deletevertex():\n");
+      printf("  Attempt to delete boundary vertex.\n");
+      internalerror();
+    }
+#endif /* SELF_CHECK */
+    edgecount++;
+    onextself(countingtri);
+  }
+
+#ifdef SELF_CHECK
+  if (edgecount < 3) {
+    printf("Internal error in deletevertex():\n  Vertex has degree %d.\n",
+           edgecount);
+    internalerror();
+  }
+#endif /* SELF_CHECK */
+  if (edgecount > 3) {
+    /* Triangulate the polygon defined by the union of all triangles */
+    /*   adjacent to the vertex being deleted.  Check the quality of */
+    /*   the resulting triangles.                                    */
+    onext(*deltri, firstedge);
+    oprev(*deltri, lastedge);
+    triangulatepolygon(m, b, &firstedge, &lastedge, edgecount, 0,
+                       !b->nobisect);
+  }
+  /* Splice out two triangles. */
+  lprev(*deltri, deltriright);
+  dnext(*deltri, lefttri);
+  sym(lefttri, leftcasing);
+  oprev(deltriright, righttri);
+  sym(righttri, rightcasing);
+  bond(*deltri, leftcasing);
+  bond(deltriright, rightcasing);
+  tspivot(lefttri, leftsubseg);
+  if (leftsubseg.ss != m->dummysub) {
+    tsbond(*deltri, leftsubseg);
+  }
+  tspivot(righttri, rightsubseg);
+  if (rightsubseg.ss != m->dummysub) {
+    tsbond(deltriright, rightsubseg);
+  }
+
+  /* Set the new origin of `deltri' and check its quality. */
+  org(lefttri, neworg);
+  setorg(*deltri, neworg);
+  if (!b->nobisect) {
+    testtriangle(m, b, deltri);
+  }
+
+  /* Delete the two spliced-out triangles. */
+  triangledealloc(m, lefttri.tri);
+  triangledealloc(m, righttri.tri);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  undovertex()   Undo the most recent vertex insertion.                    */
+/*                                                                           */
+/*  Walks through the list of transformations (flips and a vertex insertion) */
+/*  in the reverse of the order in which they were done, and undoes them.    */
+/*  The inserted vertex is removed from the triangulation and deallocated.   */
+/*  Two triangles (possibly just one) are also deallocated.                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void undovertex(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void undovertex(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri fliptri;
+  struct otri botleft, botright, topright;
+  struct otri botlcasing, botrcasing, toprcasing;
+  struct otri gluetri;
+  struct osub botlsubseg, botrsubseg, toprsubseg;
+  vertex botvertex, rightvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Walk through the list of transformations (flips and a vertex insertion) */
+  /*   in the reverse of the order in which they were done, and undo them.   */
+  while (m->lastflip != (struct flipstacker *) NULL) {
+    /* Find a triangle involved in the last unreversed transformation. */
+    decode(m->lastflip->flippedtri, fliptri);
+
+    /* We are reversing one of three transformations:  a trisection of one */
+    /*   triangle into three (by inserting a vertex in the triangle), a    */
+    /*   bisection of two triangles into four (by inserting a vertex in an */
+    /*   edge), or an edge flip.                                           */
+    if (m->lastflip->prevflip == (struct flipstacker *) NULL) {
+      /* Restore a triangle that was split into three triangles, */
+      /*   so it is again one triangle.                          */
+      dprev(fliptri, botleft);
+      lnextself(botleft);
+      onext(fliptri, botright);
+      lprevself(botright);
+      sym(botleft, botlcasing);
+      sym(botright, botrcasing);
+      dest(botleft, botvertex);
+
+      setapex(fliptri, botvertex);
+      lnextself(fliptri);
+      bond(fliptri, botlcasing);
+      tspivot(botleft, botlsubseg);
+      tsbond(fliptri, botlsubseg);
+      lnextself(fliptri);
+      bond(fliptri, botrcasing);
+      tspivot(botright, botrsubseg);
+      tsbond(fliptri, botrsubseg);
+
+      /* Delete the two spliced-out triangles. */
+      triangledealloc(m, botleft.tri);
+      triangledealloc(m, botright.tri);
+    } else if (m->lastflip->prevflip == (struct flipstacker *) &insertvertex) {
+      /* Restore two triangles that were split into four triangles, */
+      /*   so they are again two triangles.                         */
+      lprev(fliptri, gluetri);
+      sym(gluetri, botright);
+      lnextself(botright);
+      sym(botright, botrcasing);
+      dest(botright, rightvertex);
+
+      setorg(fliptri, rightvertex);
+      bond(gluetri, botrcasing);
+      tspivot(botright, botrsubseg);
+      tsbond(gluetri, botrsubseg);
+
+      /* Delete the spliced-out triangle. */
+      triangledealloc(m, botright.tri);
+
+      sym(fliptri, gluetri);
+      if (gluetri.tri != m->dummytri) {
+        lnextself(gluetri);
+        dnext(gluetri, topright);
+        sym(topright, toprcasing);
+
+        setorg(gluetri, rightvertex);
+        bond(gluetri, toprcasing);
+        tspivot(topright, toprsubseg);
+        tsbond(gluetri, toprsubseg);
+
+        /* Delete the spliced-out triangle. */
+        triangledealloc(m, topright.tri);
+      }
+
+      /* This is the end of the list, sneakily encoded. */
+      m->lastflip->prevflip = (struct flipstacker *) NULL;
+    } else {
+      /* Undo an edge flip. */
+      unflip(m, b, &fliptri);
+    }
+
+    /* Go on and process the next transformation. */
+    m->lastflip = m->lastflip->prevflip;
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh transformation routines end here                     *********/
+
+/********* Divide-and-conquer Delaunay triangulation begins here     *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  The divide-and-conquer bounding box                                      */
+/*                                                                           */
+/*  I originally implemented the divide-and-conquer and incremental Delaunay */
+/*  triangulations using the edge-based data structure presented by Guibas   */
+/*  and Stolfi.  Switching to a triangle-based data structure doubled the    */
+/*  speed.  However, I had to think of a few extra tricks to maintain the    */
+/*  elegance of the original algorithms.                                     */
+/*                                                                           */
+/*  The "bounding box" used by my variant of the divide-and-conquer          */
+/*  algorithm uses one triangle for each edge of the convex hull of the      */
+/*  triangulation.  These bounding triangles all share a common apical       */
+/*  vertex, which is represented by NULL and which represents nothing.       */
+/*  The bounding triangles are linked in a circular fan about this NULL      */
+/*  vertex, and the edges on the convex hull of the triangulation appear     */
+/*  opposite the NULL vertex.  You might find it easiest to imagine that     */
+/*  the NULL vertex is a point in 3D space behind the center of the          */
+/*  triangulation, and that the bounding triangles form a sort of cone.      */
+/*                                                                           */
+/*  This bounding box makes it easy to represent degenerate cases.  For      */
+/*  instance, the triangulation of two vertices is a single edge.  This edge */
+/*  is represented by two bounding box triangles, one on each "side" of the  */
+/*  edge.  These triangles are also linked together in a fan about the NULL  */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*  The bounding box also makes it easy to traverse the convex hull, as the  */
+/*  divide-and-conquer algorithm needs to do.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertexsort()   Sort an array of vertices by x-coordinate, using the      */
+/*                 y-coordinate as a secondary key.                          */
+/*                                                                           */
+/*  Uses quicksort.  Randomized O(n log n) time.  No, I did not make any of  */
+/*  the usual quicksort mistakes.                                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexsort(vertex *sortarray, int arraysize)
+#else /* not ANSI_DECLARATORS */
+void vertexsort(sortarray, arraysize)
+vertex *sortarray;
+int arraysize;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int left, right;
+  int pivot;
+  REAL pivotx, pivoty;
+  vertex temp;
+
+  if (arraysize == 2) {
+    /* Recursive base case. */
+    if ((sortarray[0][0] > sortarray[1][0]) ||
+        ((sortarray[0][0] == sortarray[1][0]) &&
+         (sortarray[0][1] > sortarray[1][1]))) {
+      temp = sortarray[1];
+      sortarray[1] = sortarray[0];
+      sortarray[0] = temp;
+    }
+    return;
+  }
+  /* Choose a random pivot to split the array. */
+  pivot = (int) randomnation((unsigned int) arraysize);
+  pivotx = sortarray[pivot][0];
+  pivoty = sortarray[pivot][1];
+  /* Split the array. */
+  left = -1;
+  right = arraysize;
+  while (left < right) {
+    /* Search for a vertex whose x-coordinate is too large for the left. */
+    do {
+      left++;
+    } while ((left <= right) && ((sortarray[left][0] < pivotx) ||
+                                 ((sortarray[left][0] == pivotx) &&
+                                  (sortarray[left][1] < pivoty))));
+    /* Search for a vertex whose x-coordinate is too small for the right. */
+    do {
+      right--;
+    } while ((left <= right) && ((sortarray[right][0] > pivotx) ||
+                                 ((sortarray[right][0] == pivotx) &&
+                                  (sortarray[right][1] > pivoty))));
+    if (left < right) {
+      /* Swap the left and right vertices. */
+      temp = sortarray[left];
+      sortarray[left] = sortarray[right];
+      sortarray[right] = temp;
+    }
+  }
+  if (left > 1) {
+    /* Recursively sort the left subset. */
+    vertexsort(sortarray, left);
+  }
+  if (right < arraysize - 2) {
+    /* Recursively sort the right subset. */
+    vertexsort(&sortarray[right + 1], arraysize - right - 1);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertexmedian()   An order statistic algorithm, almost.  Shuffles an      */
+/*                   array of vertices so that the first `median' vertices   */
+/*                   occur lexicographically before the remaining vertices.  */
+/*                                                                           */
+/*  Uses the x-coordinate as the primary key if axis == 0; the y-coordinate  */
+/*  if axis == 1.  Very similar to the vertexsort() procedure, but runs in   */
+/*  randomized linear time.                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexmedian(vertex *sortarray, int arraysize, int median, int axis)
+#else /* not ANSI_DECLARATORS */
+void vertexmedian(sortarray, arraysize, median, axis)
+vertex *sortarray;
+int arraysize;
+int median;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int left, right;
+  int pivot;
+  REAL pivot1, pivot2;
+  vertex temp;
+
+  if (arraysize == 2) {
+    /* Recursive base case. */
+    if ((sortarray[0][axis] > sortarray[1][axis]) ||
+        ((sortarray[0][axis] == sortarray[1][axis]) &&
+         (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) {
+      temp = sortarray[1];
+      sortarray[1] = sortarray[0];
+      sortarray[0] = temp;
+    }
+    return;
+  }
+  /* Choose a random pivot to split the array. */
+  pivot = (int) randomnation((unsigned int) arraysize);
+  pivot1 = sortarray[pivot][axis];
+  pivot2 = sortarray[pivot][1 - axis];
+  /* Split the array. */
+  left = -1;
+  right = arraysize;
+  while (left < right) {
+    /* Search for a vertex whose x-coordinate is too large for the left. */
+    do {
+      left++;
+    } while ((left <= right) && ((sortarray[left][axis] < pivot1) ||
+                                 ((sortarray[left][axis] == pivot1) &&
+                                  (sortarray[left][1 - axis] < pivot2))));
+    /* Search for a vertex whose x-coordinate is too small for the right. */
+    do {
+      right--;
+    } while ((left <= right) && ((sortarray[right][axis] > pivot1) ||
+                                 ((sortarray[right][axis] == pivot1) &&
+                                  (sortarray[right][1 - axis] > pivot2))));
+    if (left < right) {
+      /* Swap the left and right vertices. */
+      temp = sortarray[left];
+      sortarray[left] = sortarray[right];
+      sortarray[right] = temp;
+    }
+  }
+  /* Unlike in vertexsort(), at most one of the following */
+  /*   conditionals is true.                             */
+  if (left > median) {
+    /* Recursively shuffle the left subset. */
+    vertexmedian(sortarray, left, median, axis);
+  }
+  if (right < median - 1) {
+    /* Recursively shuffle the right subset. */
+    vertexmedian(&sortarray[right + 1], arraysize - right - 1,
+                 median - right - 1, axis);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  alternateaxes()   Sorts the vertices as appropriate for the divide-and-  */
+/*                    conquer algorithm with alternating cuts.               */
+/*                                                                           */
+/*  Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1.   */
+/*  For the base case, subsets containing only two or three vertices are     */
+/*  always sorted by x-coordinate.                                           */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void alternateaxes(vertex *sortarray, int arraysize, int axis)
+#else /* not ANSI_DECLARATORS */
+void alternateaxes(sortarray, arraysize, axis)
+vertex *sortarray;
+int arraysize;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int divider;
+
+  divider = arraysize >> 1;
+  if (arraysize <= 3) {
+    /* Recursive base case:  subsets of two or three vertices will be    */
+    /*   handled specially, and should always be sorted by x-coordinate. */
+    axis = 0;
+  }
+  /* Partition with a horizontal or vertical cut. */
+  vertexmedian(sortarray, arraysize, divider, axis);
+  /* Recursively partition the subsets with a cross cut. */
+  if (arraysize - divider >= 2) {
+    if (divider >= 2) {
+      alternateaxes(sortarray, divider, 1 - axis);
+    }
+    alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  mergehulls()   Merge two adjacent Delaunay triangulations into a         */
+/*                 single Delaunay triangulation.                            */
+/*                                                                           */
+/*  This is similar to the algorithm given by Guibas and Stolfi, but uses    */
+/*  a triangle-based, rather than edge-based, data structure.                */
+/*                                                                           */
+/*  The algorithm walks up the gap between the two triangulations, knitting  */
+/*  them together.  As they are merged, some of their bounding triangles     */
+/*  are converted into real triangles of the triangulation.  The procedure   */
+/*  pulls each hull's bounding triangles apart, then knits them together     */
+/*  like the teeth of two gears.  The Delaunay property determines, at each  */
+/*  step, whether the next "tooth" is a bounding triangle of the left hull   */
+/*  or the right.  When a bounding triangle becomes real, its apex is        */
+/*  changed from NULL to a real vertex.                                      */
+/*                                                                           */
+/*  Only two new triangles need to be allocated.  These become new bounding  */
+/*  triangles at the top and bottom of the seam.  They are used to connect   */
+/*  the remaining bounding triangles (those that have not been converted     */
+/*  into real triangles) into a single fan.                                  */
+/*                                                                           */
+/*  On entry, `farleft' and `innerleft' are bounding triangles of the left   */
+/*  triangulation.  The origin of `farleft' is the leftmost vertex, and      */
+/*  the destination of `innerleft' is the rightmost vertex of the            */
+/*  triangulation.  Similarly, `innerright' and `farright' are bounding      */
+/*  triangles of the right triangulation.  The origin of `innerright' and    */
+/*  destination of `farright' are the leftmost and rightmost vertices.       */
+/*                                                                           */
+/*  On completion, the origin of `farleft' is the leftmost vertex of the     */
+/*  merged triangulation, and the destination of `farright' is the rightmost */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft,
+                struct otri *innerleft, struct otri *innerright,
+                struct otri *farright, int axis)
+#else /* not ANSI_DECLARATORS */
+void mergehulls(m, b, farleft, innerleft, innerright, farright, axis)
+struct mesh *m;
+struct behavior *b;
+struct otri *farleft;
+struct otri *innerleft;
+struct otri *innerright;
+struct otri *farright;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri leftcand, rightcand;
+  struct otri baseedge;
+  struct otri nextedge;
+  struct otri sidecasing, topcasing, outercasing;
+  struct otri checkedge;
+  vertex innerleftdest;
+  vertex innerrightorg;
+  vertex innerleftapex, innerrightapex;
+  vertex farleftpt, farrightpt;
+  vertex farleftapex, farrightapex;
+  vertex lowerleft, lowerright;
+  vertex upperleft, upperright;
+  vertex nextapex;
+  vertex checkvertex;
+  int changemade;
+  int badedge;
+  int leftfinished, rightfinished;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  dest(*innerleft, innerleftdest);
+  apex(*innerleft, innerleftapex);
+  org(*innerright, innerrightorg);
+  apex(*innerright, innerrightapex);
+  /* Special treatment for horizontal cuts. */
+  if (b->dwyer && (axis == 1)) {
+    org(*farleft, farleftpt);
+    apex(*farleft, farleftapex);
+    dest(*farright, farrightpt);
+    apex(*farright, farrightapex);
+    /* The pointers to the extremal vertices are shifted to point to the */
+    /*   topmost and bottommost vertex of each hull, rather than the     */
+    /*   leftmost and rightmost vertices.                                */
+    while (farleftapex[1] < farleftpt[1]) {
+      lnextself(*farleft);
+      symself(*farleft);
+      farleftpt = farleftapex;
+      apex(*farleft, farleftapex);
+    }
+    sym(*innerleft, checkedge);
+    apex(checkedge, checkvertex);
+    while (checkvertex[1] > innerleftdest[1]) {
+      lnext(checkedge, *innerleft);
+      innerleftapex = innerleftdest;
+      innerleftdest = checkvertex;
+      sym(*innerleft, checkedge);
+      apex(checkedge, checkvertex);
+    }
+    while (innerrightapex[1] < innerrightorg[1]) {
+      lnextself(*innerright);
+      symself(*innerright);
+      innerrightorg = innerrightapex;
+      apex(*innerright, innerrightapex);
+    }
+    sym(*farright, checkedge);
+    apex(checkedge, checkvertex);
+    while (checkvertex[1] > farrightpt[1]) {
+      lnext(checkedge, *farright);
+      farrightapex = farrightpt;
+      farrightpt = checkvertex;
+      sym(*farright, checkedge);
+      apex(checkedge, checkvertex);
+    }
+  }
+  /* Find a line tangent to and below both hulls. */
+  do {
+    changemade = 0;
+    /* Make innerleftdest the "bottommost" vertex of the left hull. */
+    if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) >
+        0.0) {
+      lprevself(*innerleft);
+      symself(*innerleft);
+      innerleftdest = innerleftapex;
+      apex(*innerleft, innerleftapex);
+      changemade = 1;
+    }
+    /* Make innerrightorg the "bottommost" vertex of the right hull. */
+    if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) >
+        0.0) {
+      lnextself(*innerright);
+      symself(*innerright);
+      innerrightorg = innerrightapex;
+      apex(*innerright, innerrightapex);
+      changemade = 1;
+    }
+  } while (changemade);
+  /* Find the two candidates to be the next "gear tooth." */
+  sym(*innerleft, leftcand);
+  sym(*innerright, rightcand);
+  /* Create the bottom new bounding triangle. */
+  maketriangle(m, b, &baseedge);
+  /* Connect it to the bounding boxes of the left and right triangulations. */
+  bond(baseedge, *innerleft);
+  lnextself(baseedge);
+  bond(baseedge, *innerright);
+  lnextself(baseedge);
+  setorg(baseedge, innerrightorg);
+  setdest(baseedge, innerleftdest);
+  /* Apex is intentionally left NULL. */
+  if (b->verbose > 2) {
+    printf("  Creating base bounding ");
+    printtriangle(m, b, &baseedge);
+  }
+  /* Fix the extreme triangles if necessary. */
+  org(*farleft, farleftpt);
+  if (innerleftdest == farleftpt) {
+    lnext(baseedge, *farleft);
+  }
+  dest(*farright, farrightpt);
+  if (innerrightorg == farrightpt) {
+    lprev(baseedge, *farright);
+  }
+  /* The vertices of the current knitting edge. */
+  lowerleft = innerleftdest;
+  lowerright = innerrightorg;
+  /* The candidate vertices for knitting. */
+  apex(leftcand, upperleft);
+  apex(rightcand, upperright);
+  /* Walk up the gap between the two triangulations, knitting them together. */
+  while (1) {
+    /* Have we reached the top?  (This isn't quite the right question,       */
+    /*   because even though the left triangulation might seem finished now, */
+    /*   moving up on the right triangulation might reveal a new vertex of   */
+    /*   the left triangulation.  And vice-versa.)                           */
+    leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <=
+                   0.0;
+    rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright)
+                 <= 0.0;
+    if (leftfinished && rightfinished) {
+      /* Create the top new bounding triangle. */
+      maketriangle(m, b, &nextedge);
+      setorg(nextedge, lowerleft);
+      setdest(nextedge, lowerright);
+      /* Apex is intentionally left NULL. */
+      /* Connect it to the bounding boxes of the two triangulations. */
+      bond(nextedge, baseedge);
+      lnextself(nextedge);
+      bond(nextedge, rightcand);
+      lnextself(nextedge);
+      bond(nextedge, leftcand);
+      if (b->verbose > 2) {
+        printf("  Creating top bounding ");
+        printtriangle(m, b, &nextedge);
+      }
+      /* Special treatment for horizontal cuts. */
+      if (b->dwyer && (axis == 1)) {
+        org(*farleft, farleftpt);
+        apex(*farleft, farleftapex);
+        dest(*farright, farrightpt);
+        apex(*farright, farrightapex);
+        sym(*farleft, checkedge);
+        apex(checkedge, checkvertex);
+        /* The pointers to the extremal vertices are restored to the  */
+        /*   leftmost and rightmost vertices (rather than topmost and */
+        /*   bottommost).                                             */
+        while (checkvertex[0] < farleftpt[0]) {
+          lprev(checkedge, *farleft);
+          farleftapex = farleftpt;
+          farleftpt = checkvertex;
+          sym(*farleft, checkedge);
+          apex(checkedge, checkvertex);
+        }
+        while (farrightapex[0] > farrightpt[0]) {
+          lprevself(*farright);
+          symself(*farright);
+          farrightpt = farrightapex;
+          apex(*farright, farrightapex);
+        }
+      }
+      return;
+    }
+    /* Consider eliminating edges from the left triangulation. */
+    if (!leftfinished) {
+      /* What vertex would be exposed if an edge were deleted? */
+      lprev(leftcand, nextedge);
+      symself(nextedge);
+      apex(nextedge, nextapex);
+      /* If nextapex is NULL, then no vertex would be exposed; the */
+      /*   triangulation would have been eaten right through.      */
+      if (nextapex != (vertex) NULL) {
+        /* Check whether the edge is Delaunay. */
+        badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) >
+                  0.0;
+        while (badedge) {
+          /* Eliminate the edge with an edge flip.  As a result, the    */
+          /*   left triangulation will have one more boundary triangle. */
+          lnextself(nextedge);
+          sym(nextedge, topcasing);
+          lnextself(nextedge);
+          sym(nextedge, sidecasing);
+          bond(nextedge, topcasing);
+          bond(leftcand, sidecasing);
+          lnextself(leftcand);
+          sym(leftcand, outercasing);
+          lprevself(nextedge);
+          bond(nextedge, outercasing);
+          /* Correct the vertices to reflect the edge flip. */
+          setorg(leftcand, lowerleft);
+          setdest(leftcand, NULL);
+          setapex(leftcand, nextapex);
+          setorg(nextedge, NULL);
+          setdest(nextedge, upperleft);
+          setapex(nextedge, nextapex);
+          /* Consider the newly exposed vertex. */
+          upperleft = nextapex;
+          /* What vertex would be exposed if another edge were deleted? */
+          otricopy(sidecasing, nextedge);
+          apex(nextedge, nextapex);
+          if (nextapex != (vertex) NULL) {
+            /* Check whether the edge is Delaunay. */
+            badedge = incircle(m, b, lowerleft, lowerright, upperleft,
+                               nextapex) > 0.0;
+          } else {
+            /* Avoid eating right through the triangulation. */
+            badedge = 0;
+          }
+        }
+      }
+    }
+    /* Consider eliminating edges from the right triangulation. */
+    if (!rightfinished) {
+      /* What vertex would be exposed if an edge were deleted? */
+      lnext(rightcand, nextedge);
+      symself(nextedge);
+      apex(nextedge, nextapex);
+      /* If nextapex is NULL, then no vertex would be exposed; the */
+      /*   triangulation would have been eaten right through.      */
+      if (nextapex != (vertex) NULL) {
+        /* Check whether the edge is Delaunay. */
+        badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) >
+                  0.0;
+        while (badedge) {
+          /* Eliminate the edge with an edge flip.  As a result, the     */
+          /*   right triangulation will have one more boundary triangle. */
+          lprevself(nextedge);
+          sym(nextedge, topcasing);
+          lprevself(nextedge);
+          sym(nextedge, sidecasing);
+          bond(nextedge, topcasing);
+          bond(rightcand, sidecasing);
+          lprevself(rightcand);
+          sym(rightcand, outercasing);
+          lnextself(nextedge);
+          bond(nextedge, outercasing);
+          /* Correct the vertices to reflect the edge flip. */
+          setorg(rightcand, NULL);
+          setdest(rightcand, lowerright);
+          setapex(rightcand, nextapex);
+          setorg(nextedge, upperright);
+          setdest(nextedge, NULL);
+          setapex(nextedge, nextapex);
+          /* Consider the newly exposed vertex. */
+          upperright = nextapex;
+          /* What vertex would be exposed if another edge were deleted? */
+          otricopy(sidecasing, nextedge);
+          apex(nextedge, nextapex);
+          if (nextapex != (vertex) NULL) {
+            /* Check whether the edge is Delaunay. */
+            badedge = incircle(m, b, lowerleft, lowerright, upperright,
+                               nextapex) > 0.0;
+          } else {
+            /* Avoid eating right through the triangulation. */
+            badedge = 0;
+          }
+        }
+      }
+    }
+    if (leftfinished || (!rightfinished &&
+           (incircle(m, b, upperleft, lowerleft, lowerright, upperright) >
+            0.0))) {
+      /* Knit the triangulations, adding an edge from `lowerleft' */
+      /*   to `upperright'.                                       */
+      bond(baseedge, rightcand);
+      lprev(rightcand, baseedge);
+      setdest(baseedge, lowerleft);
+      lowerright = upperright;
+      sym(baseedge, rightcand);
+      apex(rightcand, upperright);
+    } else {
+      /* Knit the triangulations, adding an edge from `upperleft' */
+      /*   to `lowerright'.                                       */
+      bond(baseedge, leftcand);
+      lnext(leftcand, baseedge);
+      setorg(baseedge, lowerright);
+      lowerleft = upperleft;
+      sym(baseedge, leftcand);
+      apex(leftcand, upperleft);
+    }
+    if (b->verbose > 2) {
+      printf("  Connecting ");
+      printtriangle(m, b, &baseedge);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  divconqrecurse()   Recursively form a Delaunay triangulation by the      */
+/*                     divide-and-conquer method.                            */
+/*                                                                           */
+/*  Recursively breaks down the problem into smaller pieces, which are       */
+/*  knitted together by mergehulls().  The base cases (problems of two or    */
+/*  three vertices) are handled specially here.                              */
+/*                                                                           */
+/*  On completion, `farleft' and `farright' are bounding triangles such that */
+/*  the origin of `farleft' is the leftmost vertex (breaking ties by         */
+/*  choosing the highest leftmost vertex), and the destination of            */
+/*  `farright' is the rightmost vertex (breaking ties by choosing the        */
+/*  lowest rightmost vertex).                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray,
+                    int vertices, int axis,
+                    struct otri *farleft, struct otri *farright)
+#else /* not ANSI_DECLARATORS */
+void divconqrecurse(m, b, sortarray, vertices, axis, farleft, farright)
+struct mesh *m;
+struct behavior *b;
+vertex *sortarray;
+int vertices;
+int axis;
+struct otri *farleft;
+struct otri *farright;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri midtri, tri1, tri2, tri3;
+  struct otri innerleft, innerright;
+  REAL area;
+  int divider;
+
+  if (b->verbose > 2) {
+    printf("  Triangulating %d vertices.\n", vertices);
+  }
+  if (vertices == 2) {
+    /* The triangulation of two vertices is an edge.  An edge is */
+    /*   represented by two bounding triangles.                  */
+    maketriangle(m, b, farleft);
+    setorg(*farleft, sortarray[0]);
+    setdest(*farleft, sortarray[1]);
+    /* The apex is intentionally left NULL. */
+    maketriangle(m, b, farright);
+    setorg(*farright, sortarray[1]);
+    setdest(*farright, sortarray[0]);
+    /* The apex is intentionally left NULL. */
+    bond(*farleft, *farright);
+    lprevself(*farleft);
+    lnextself(*farright);
+    bond(*farleft, *farright);
+    lprevself(*farleft);
+    lnextself(*farright);
+    bond(*farleft, *farright);
+    if (b->verbose > 2) {
+      printf("  Creating ");
+      printtriangle(m, b, farleft);
+      printf("  Creating ");
+      printtriangle(m, b, farright);
+    }
+    /* Ensure that the origin of `farleft' is sortarray[0]. */
+    lprev(*farright, *farleft);
+    return;
+  } else if (vertices == 3) {
+    /* The triangulation of three vertices is either a triangle (with */
+    /*   three bounding triangles) or two edges (with four bounding   */
+    /*   triangles).  In either case, four triangles are created.     */
+    maketriangle(m, b, &midtri);
+    maketriangle(m, b, &tri1);
+    maketriangle(m, b, &tri2);
+    maketriangle(m, b, &tri3);
+    area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]);
+    if (area == 0.0) {
+      /* Three collinear vertices; the triangulation is two edges. */
+      setorg(midtri, sortarray[0]);
+      setdest(midtri, sortarray[1]);
+      setorg(tri1, sortarray[1]);
+      setdest(tri1, sortarray[0]);
+      setorg(tri2, sortarray[2]);
+      setdest(tri2, sortarray[1]);
+      setorg(tri3, sortarray[1]);
+      setdest(tri3, sortarray[2]);
+      /* All apices are intentionally left NULL. */
+      bond(midtri, tri1);
+      bond(tri2, tri3);
+      lnextself(midtri);
+      lprevself(tri1);
+      lnextself(tri2);
+      lprevself(tri3);
+      bond(midtri, tri3);
+      bond(tri1, tri2);
+      lnextself(midtri);
+      lprevself(tri1);
+      lnextself(tri2);
+      lprevself(tri3);
+      bond(midtri, tri1);
+      bond(tri2, tri3);
+      /* Ensure that the origin of `farleft' is sortarray[0]. */
+      otricopy(tri1, *farleft);
+      /* Ensure that the destination of `farright' is sortarray[2]. */
+      otricopy(tri2, *farright);
+    } else {
+      /* The three vertices are not collinear; the triangulation is one */
+      /*   triangle, namely `midtri'.                                   */
+      setorg(midtri, sortarray[0]);
+      setdest(tri1, sortarray[0]);
+      setorg(tri3, sortarray[0]);
+      /* Apices of tri1, tri2, and tri3 are left NULL. */
+      if (area > 0.0) {
+        /* The vertices are in counterclockwise order. */
+        setdest(midtri, sortarray[1]);
+        setorg(tri1, sortarray[1]);
+        setdest(tri2, sortarray[1]);
+        setapex(midtri, sortarray[2]);
+        setorg(tri2, sortarray[2]);
+        setdest(tri3, sortarray[2]);
+      } else {
+        /* The vertices are in clockwise order. */
+        setdest(midtri, sortarray[2]);
+        setorg(tri1, sortarray[2]);
+        setdest(tri2, sortarray[2]);
+        setapex(midtri, sortarray[1]);
+        setorg(tri2, sortarray[1]);
+        setdest(tri3, sortarray[1]);
+      }
+      /* The topology does not depend on how the vertices are ordered. */
+      bond(midtri, tri1);
+      lnextself(midtri);
+      bond(midtri, tri2);
+      lnextself(midtri);
+      bond(midtri, tri3);
+      lprevself(tri1);
+      lnextself(tri2);
+      bond(tri1, tri2);
+      lprevself(tri1);
+      lprevself(tri3);
+      bond(tri1, tri3);
+      lnextself(tri2);
+      lprevself(tri3);
+      bond(tri2, tri3);
+      /* Ensure that the origin of `farleft' is sortarray[0]. */
+      otricopy(tri1, *farleft);
+      /* Ensure that the destination of `farright' is sortarray[2]. */
+      if (area > 0.0) {
+        otricopy(tri2, *farright);
+      } else {
+        lnext(*farleft, *farright);
+      }
+    }
+    if (b->verbose > 2) {
+      printf("  Creating ");
+      printtriangle(m, b, &midtri);
+      printf("  Creating ");
+      printtriangle(m, b, &tri1);
+      printf("  Creating ");
+      printtriangle(m, b, &tri2);
+      printf("  Creating ");
+      printtriangle(m, b, &tri3);
+    }
+    return;
+  } else {
+    /* Split the vertices in half. */
+    divider = vertices >> 1;
+    /* Recursively triangulate each half. */
+    divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft);
+    divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis,
+                   &innerright, farright);
+    if (b->verbose > 1) {
+      printf("  Joining triangulations with %d and %d vertices.\n", divider,
+             vertices - divider);
+    }
+    /* Merge the two triangulations into one. */
+    mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis);
+  }
+}
+
+#ifdef ANSI_DECLARATORS
+long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost)
+#else /* not ANSI_DECLARATORS */
+long removeghosts(m, b, startghost)
+struct mesh *m;
+struct behavior *b;
+struct otri *startghost;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchedge;
+  struct otri dissolveedge;
+  struct otri deadtriangle;
+  vertex markorg;
+  long hullsize;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose) {
+    printf("  Removing ghost triangles.\n");
+  }
+  /* Find an edge on the convex hull to start point location from. */
+  lprev(*startghost, searchedge);
+  symself(searchedge);
+  m->dummytri[0] = encode(searchedge);
+  /* Remove the bounding box and count the convex hull edges. */
+  otricopy(*startghost, dissolveedge);
+  hullsize = 0;
+  do {
+    hullsize++;
+    lnext(dissolveedge, deadtriangle);
+    lprevself(dissolveedge);
+    symself(dissolveedge);
+    /* If no PSLG is involved, set the boundary markers of all the vertices */
+    /*   on the convex hull.  If a PSLG is used, this step is done later.   */
+    if (!b->poly) {
+      /* Watch out for the case where all the input vertices are collinear. */
+      if (dissolveedge.tri != m->dummytri) {
+        org(dissolveedge, markorg);
+        if (vertexmark(markorg) == 0) {
+          setvertexmark(markorg, 1);
+        }
+      }
+    }
+    /* Remove a bounding triangle from a convex hull triangle. */
+    dissolve(dissolveedge);
+    /* Find the next bounding triangle. */
+    sym(deadtriangle, dissolveedge);
+    /* Delete the bounding triangle. */
+    triangledealloc(m, deadtriangle.tri);
+  } while (!otriequal(dissolveedge, *startghost));
+  return hullsize;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  divconqdelaunay()   Form a Delaunay triangulation by the divide-and-     */
+/*                      conquer method.                                      */
+/*                                                                           */
+/*  Sorts the vertices, calls a recursive procedure to triangulate them, and */
+/*  removes the bounding box, setting boundary markers as appropriate.       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+long divconqdelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long divconqdelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex *sortarray;
+  struct otri hullleft, hullright;
+  int divider;
+  int i, j;
+
+  if (b->verbose) {
+    printf("  Sorting vertices.\n");
+  }
+
+  /* Allocate an array of pointers to vertices for sorting. */
+  sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex));
+  traversalinit(&m->vertices);
+  for (i = 0; i < m->invertices; i++) {
+    sortarray[i] = vertextraverse(m);
+  }
+  /* Sort the vertices. */
+  vertexsort(sortarray, m->invertices);
+  /* Discard duplicate vertices, which can really mess up the algorithm. */
+  i = 0;
+  for (j = 1; j < m->invertices; j++) {
+    if ((sortarray[i][0] == sortarray[j][0])
+        && (sortarray[i][1] == sortarray[j][1])) {
+      if (!b->quiet) {
+        printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+               sortarray[j][0], sortarray[j][1]);
+      }
+      setvertextype(sortarray[j], UNDEADVERTEX);
+      m->undeads++;
+    } else {
+      i++;
+      sortarray[i] = sortarray[j];
+    }
+  }
+  i++;
+  if (b->dwyer) {
+    /* Re-sort the array of vertices to accommodate alternating cuts. */
+    divider = i >> 1;
+    if (i - divider >= 2) {
+      if (divider >= 2) {
+        alternateaxes(sortarray, divider, 1);
+      }
+      alternateaxes(&sortarray[divider], i - divider, 1);
+    }
+  }
+
+  if (b->verbose) {
+    printf("  Forming triangulation.\n");
+  }
+
+  /* Form the Delaunay triangulation. */
+  divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright);
+  trifree((VOID *) sortarray);
+
+  return removeghosts(m, b, &hullleft);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Divide-and-conquer Delaunay triangulation ends here       *********/
+
+/********* Incremental Delaunay triangulation begins here            *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  boundingbox()   Form an "infinite" bounding triangle to insert vertices  */
+/*                  into.                                                    */
+/*                                                                           */
+/*  The vertices at "infinity" are assigned finite coordinates, which are    */
+/*  used by the point location routines, but (mostly) ignored by the         */
+/*  Delaunay edge flip routines.                                             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void boundingbox(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void boundingbox(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri inftri;          /* Handle for the triangular bounding box. */
+  REAL width;
+
+  if (b->verbose) {
+    printf("  Creating triangular bounding box.\n");
+  }
+  /* Find the width (or height, whichever is larger) of the triangulation. */
+  width = m->xmax - m->xmin;
+  if (m->ymax - m->ymin > width) {
+    width = m->ymax - m->ymin;
+  }
+  if (width == 0.0) {
+    width = 1.0;
+  }
+  /* Create the vertices of the bounding box. */
+  m->infvertex1 = (vertex) trimalloc(m->vertices.itembytes);
+  m->infvertex2 = (vertex) trimalloc(m->vertices.itembytes);
+  m->infvertex3 = (vertex) trimalloc(m->vertices.itembytes);
+  m->infvertex1[0] = m->xmin - 50.0 * width;
+  m->infvertex1[1] = m->ymin - 40.0 * width;
+  m->infvertex2[0] = m->xmax + 50.0 * width;
+  m->infvertex2[1] = m->ymin - 40.0 * width;
+  m->infvertex3[0] = 0.5 * (m->xmin + m->xmax);
+  m->infvertex3[1] = m->ymax + 60.0 * width;
+
+  /* Create the bounding box. */
+  maketriangle(m, b, &inftri);
+  setorg(inftri, m->infvertex1);
+  setdest(inftri, m->infvertex2);
+  setapex(inftri, m->infvertex3);
+  /* Link dummytri to the bounding box so we can always find an */
+  /*   edge to begin searching (point location) from.           */
+  m->dummytri[0] = (triangle) inftri.tri;
+  if (b->verbose > 2) {
+    printf("  Creating ");
+    printtriangle(m, b, &inftri);
+  }
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  removebox()   Remove the "infinite" bounding triangle, setting boundary  */
+/*                markers as appropriate.                                    */
+/*                                                                           */
+/*  The triangular bounding box has three boundary triangles (one for each   */
+/*  side of the bounding box), and a bunch of triangles fanning out from     */
+/*  the three bounding box vertices (one triangle for each edge of the       */
+/*  convex hull of the inner mesh).  This routine removes these triangles.   */
+/*                                                                           */
+/*  Returns the number of edges on the convex hull of the triangulation.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long removebox(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long removebox(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri deadtriangle;
+  struct otri searchedge;
+  struct otri checkedge;
+  struct otri nextedge, finaledge, dissolveedge;
+  vertex markorg;
+  long hullsize;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose) {
+    printf("  Removing triangular bounding box.\n");
+  }
+  /* Find a boundary triangle. */
+  nextedge.tri = m->dummytri;
+  nextedge.orient = 0;
+  symself(nextedge);
+  /* Mark a place to stop. */
+  lprev(nextedge, finaledge);
+  lnextself(nextedge);
+  symself(nextedge);
+  /* Find a triangle (on the boundary of the vertex set) that isn't */
+  /*   a bounding box triangle.                                     */
+  lprev(nextedge, searchedge);
+  symself(searchedge);
+  /* Check whether nextedge is another boundary triangle */
+  /*   adjacent to the first one.                        */
+  lnext(nextedge, checkedge);
+  symself(checkedge);
+  if (checkedge.tri == m->dummytri) {
+    /* Go on to the next triangle.  There are only three boundary   */
+    /*   triangles, and this next triangle cannot be the third one, */
+    /*   so it's safe to stop here.                                 */
+    lprevself(searchedge);
+    symself(searchedge);
+  }
+  /* Find a new boundary edge to search from, as the current search */
+  /*   edge lies on a bounding box triangle and will be deleted.    */
+  m->dummytri[0] = encode(searchedge);
+  hullsize = -2l;
+  while (!otriequal(nextedge, finaledge)) {
+    hullsize++;
+    lprev(nextedge, dissolveedge);
+    symself(dissolveedge);
+    /* If not using a PSLG, the vertices should be marked now. */
+    /*   (If using a PSLG, markhull() will do the job.)        */
+    if (!b->poly) {
+      /* Be careful!  One must check for the case where all the input     */
+      /*   vertices are collinear, and thus all the triangles are part of */
+      /*   the bounding box.  Otherwise, the setvertexmark() call below   */
+      /*   will cause a bad pointer reference.                            */
+      if (dissolveedge.tri != m->dummytri) {
+        org(dissolveedge, markorg);
+        if (vertexmark(markorg) == 0) {
+          setvertexmark(markorg, 1);
+        }
+      }
+    }
+    /* Disconnect the bounding box triangle from the mesh triangle. */
+    dissolve(dissolveedge);
+    lnext(nextedge, deadtriangle);
+    sym(deadtriangle, nextedge);
+    /* Get rid of the bounding box triangle. */
+    triangledealloc(m, deadtriangle.tri);
+    /* Do we need to turn the corner? */
+    if (nextedge.tri == m->dummytri) {
+      /* Turn the corner. */
+      otricopy(dissolveedge, nextedge);
+    }
+  }
+  triangledealloc(m, finaledge.tri);
+
+  trifree((VOID *) m->infvertex1);  /* Deallocate the bounding box vertices. */
+  trifree((VOID *) m->infvertex2);
+  trifree((VOID *) m->infvertex3);
+
+  return hullsize;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  incrementaldelaunay()   Form a Delaunay triangulation by incrementally   */
+/*                          inserting vertices.                              */
+/*                                                                           */
+/*  Returns the number of edges on the convex hull of the triangulation.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long incrementaldelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long incrementaldelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri starttri;
+  vertex vertexloop;
+
+  /* Create a triangular bounding box. */
+  boundingbox(m, b);
+  if (b->verbose) {
+    printf("  Incrementally inserting vertices.\n");
+  }
+  traversalinit(&m->vertices);
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    starttri.tri = m->dummytri;
+    if (insertvertex(m, b, vertexloop, &starttri, (struct osub *) NULL, 0, 0,
+                     0.0) == DUPLICATEVERTEX) {
+      if (!b->quiet) {
+        printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+               vertexloop[0], vertexloop[1]);
+      }
+      setvertextype(vertexloop, UNDEADVERTEX);
+      m->undeads++;
+    }
+    vertexloop = vertextraverse(m);
+  }
+  /* Remove the bounding box. */
+  return removebox(m, b);
+}
+
+#endif /* not REDUCED */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Incremental Delaunay triangulation ends here              *********/
+
+/********* Sweepline Delaunay triangulation begins here              *********/
+/**                                                                         **/
+/**                                                                         **/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapinsert(struct event **heap, int heapsize, struct event *newevent)
+#else /* not ANSI_DECLARATORS */
+void eventheapinsert(heap, heapsize, newevent)
+struct event **heap;
+int heapsize;
+struct event *newevent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL eventx, eventy;
+  int eventnum;
+  int parent;
+  int notdone;
+
+  eventx = newevent->xkey;
+  eventy = newevent->ykey;
+  eventnum = heapsize;
+  notdone = eventnum > 0;
+  while (notdone) {
+    parent = (eventnum - 1) >> 1;
+    if ((heap[parent]->ykey < eventy) ||
+        ((heap[parent]->ykey == eventy)
+         && (heap[parent]->xkey <= eventx))) {
+      notdone = 0;
+    } else {
+      heap[eventnum] = heap[parent];
+      heap[eventnum]->heapposition = eventnum;
+
+      eventnum = parent;
+      notdone = eventnum > 0;
+    }
+  }
+  heap[eventnum] = newevent;
+  newevent->heapposition = eventnum;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapify(struct event **heap, int heapsize, int eventnum)
+#else /* not ANSI_DECLARATORS */
+void eventheapify(heap, heapsize, eventnum)
+struct event **heap;
+int heapsize;
+int eventnum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event *thisevent;
+  REAL eventx, eventy;
+  int leftchild, rightchild;
+  int smallest;
+  int notdone;
+
+  thisevent = heap[eventnum];
+  eventx = thisevent->xkey;
+  eventy = thisevent->ykey;
+  leftchild = 2 * eventnum + 1;
+  notdone = leftchild < heapsize;
+  while (notdone) {
+    if ((heap[leftchild]->ykey < eventy) ||
+        ((heap[leftchild]->ykey == eventy)
+         && (heap[leftchild]->xkey < eventx))) {
+      smallest = leftchild;
+    } else {
+      smallest = eventnum;
+    }
+    rightchild = leftchild + 1;
+    if (rightchild < heapsize) {
+      if ((heap[rightchild]->ykey < heap[smallest]->ykey) ||
+          ((heap[rightchild]->ykey == heap[smallest]->ykey)
+           && (heap[rightchild]->xkey < heap[smallest]->xkey))) {
+        smallest = rightchild;
+      }
+    }
+    if (smallest == eventnum) {
+      notdone = 0;
+    } else {
+      heap[eventnum] = heap[smallest];
+      heap[eventnum]->heapposition = eventnum;
+      heap[smallest] = thisevent;
+      thisevent->heapposition = smallest;
+
+      eventnum = smallest;
+      leftchild = 2 * eventnum + 1;
+      notdone = leftchild < heapsize;
+    }
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapdelete(struct event **heap, int heapsize, int eventnum)
+#else /* not ANSI_DECLARATORS */
+void eventheapdelete(heap, heapsize, eventnum)
+struct event **heap;
+int heapsize;
+int eventnum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event *moveevent;
+  REAL eventx, eventy;
+  int parent;
+  int notdone;
+
+  moveevent = heap[heapsize - 1];
+  if (eventnum > 0) {
+    eventx = moveevent->xkey;
+    eventy = moveevent->ykey;
+    do {
+      parent = (eventnum - 1) >> 1;
+      if ((heap[parent]->ykey < eventy) ||
+          ((heap[parent]->ykey == eventy)
+           && (heap[parent]->xkey <= eventx))) {
+        notdone = 0;
+      } else {
+        heap[eventnum] = heap[parent];
+        heap[eventnum]->heapposition = eventnum;
+
+        eventnum = parent;
+        notdone = eventnum > 0;
+      }
+    } while (notdone);
+  }
+  heap[eventnum] = moveevent;
+  moveevent->heapposition = eventnum;
+  eventheapify(heap, heapsize - 1, eventnum);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void createeventheap(struct mesh *m, struct event ***eventheap,
+                     struct event **events, struct event **freeevents)
+#else /* not ANSI_DECLARATORS */
+void createeventheap(m, eventheap, events, freeevents)
+struct mesh *m;
+struct event ***eventheap;
+struct event **events;
+struct event **freeevents;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex thisvertex;
+  int maxevents;
+  int i;
+
+  maxevents = (3 * m->invertices) / 2;
+  *eventheap = (struct event **)
+               trimalloc(maxevents * (int) sizeof(struct event *));
+  *events = (struct event *) trimalloc(maxevents * (int) sizeof(struct event));
+  traversalinit(&m->vertices);
+  for (i = 0; i < m->invertices; i++) {
+    thisvertex = vertextraverse(m);
+    (*events)[i].eventptr = (VOID *) thisvertex;
+    (*events)[i].xkey = thisvertex[0];
+    (*events)[i].ykey = thisvertex[1];
+    eventheapinsert(*eventheap, i, *events + i);
+  }
+  *freeevents = (struct event *) NULL;
+  for (i = maxevents - 1; i >= m->invertices; i--) {
+    (*events)[i].eventptr = (VOID *) *freeevents;
+    *freeevents = *events + i;
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+int rightofhyperbola(struct mesh *m, struct otri *fronttri, vertex newsite)
+#else /* not ANSI_DECLARATORS */
+int rightofhyperbola(m, fronttri, newsite)
+struct mesh *m;
+struct otri *fronttri;
+vertex newsite;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex leftvertex, rightvertex;
+  REAL dxa, dya, dxb, dyb;
+
+  m->hyperbolacount++;
+
+  dest(*fronttri, leftvertex);
+  apex(*fronttri, rightvertex);
+  if ((leftvertex[1] < rightvertex[1]) ||
+      ((leftvertex[1] == rightvertex[1]) &&
+       (leftvertex[0] < rightvertex[0]))) {
+    if (newsite[0] >= rightvertex[0]) {
+      return 1;
+    }
+  } else {
+    if (newsite[0] <= leftvertex[0]) {
+      return 0;
+    }
+  }
+  dxa = leftvertex[0] - newsite[0];
+  dya = leftvertex[1] - newsite[1];
+  dxb = rightvertex[0] - newsite[0];
+  dyb = rightvertex[1] - newsite[1];
+  return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+REAL circletop(struct mesh *m, vertex pa, vertex pb, vertex pc, REAL ccwabc)
+#else /* not ANSI_DECLARATORS */
+REAL circletop(m, pa, pb, pc, ccwabc)
+struct mesh *m;
+vertex pa;
+vertex pb;
+vertex pc;
+REAL ccwabc;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL xac, yac, xbc, ybc, xab, yab;
+  REAL aclen2, bclen2, ablen2;
+
+  m->circletopcount++;
+
+  xac = pa[0] - pc[0];
+  yac = pa[1] - pc[1];
+  xbc = pb[0] - pc[0];
+  ybc = pb[1] - pc[1];
+  xab = pa[0] - pb[0];
+  yab = pa[1] - pb[1];
+  aclen2 = xac * xac + yac * yac;
+  bclen2 = xbc * xbc + ybc * ybc;
+  ablen2 = xab * xab + yab * yab;
+  return pc[1] + (xac * bclen2 - xbc * aclen2 + sqrt(aclen2 * bclen2 * ablen2))
+               / (2.0 * ccwabc);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void check4deadevent(struct otri *checktri, struct event **freeevents,
+                     struct event **eventheap, int *heapsize)
+#else /* not ANSI_DECLARATORS */
+void check4deadevent(checktri, freeevents, eventheap, heapsize)
+struct otri *checktri;
+struct event **freeevents;
+struct event **eventheap;
+int *heapsize;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event *deadevent;
+  vertex eventvertex;
+  int eventnum;
+
+  org(*checktri, eventvertex);
+  if (eventvertex != (vertex) NULL) {
+    deadevent = (struct event *) eventvertex;
+    eventnum = deadevent->heapposition;
+    deadevent->eventptr = (VOID *) *freeevents;
+    *freeevents = deadevent;
+    eventheapdelete(eventheap, *heapsize, eventnum);
+    (*heapsize)--;
+    setorg(*checktri, NULL);
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *splay(struct mesh *m, struct splaynode *splaytree,
+                        vertex searchpoint, struct otri *searchtri)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *splay(m, splaytree, searchpoint, searchtri)
+struct mesh *m;
+struct splaynode *splaytree;
+vertex searchpoint;
+struct otri *searchtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct splaynode *child, *grandchild;
+  struct splaynode *lefttree, *righttree;
+  struct splaynode *leftright;
+  vertex checkvertex;
+  int rightofroot, rightofchild;
+
+  if (splaytree == (struct splaynode *) NULL) {
+    return (struct splaynode *) NULL;
+  }
+  dest(splaytree->keyedge, checkvertex);
+  if (checkvertex == splaytree->keydest) {
+    rightofroot = rightofhyperbola(m, &splaytree->keyedge, searchpoint);
+    if (rightofroot) {
+      otricopy(splaytree->keyedge, *searchtri);
+      child = splaytree->rchild;
+    } else {
+      child = splaytree->lchild;
+    }
+    if (child == (struct splaynode *) NULL) {
+      return splaytree;
+    }
+    dest(child->keyedge, checkvertex);
+    if (checkvertex != child->keydest) {
+      child = splay(m, child, searchpoint, searchtri);
+      if (child == (struct splaynode *) NULL) {
+        if (rightofroot) {
+          splaytree->rchild = (struct splaynode *) NULL;
+        } else {
+          splaytree->lchild = (struct splaynode *) NULL;
+        }
+        return splaytree;
+      }
+    }
+    rightofchild = rightofhyperbola(m, &child->keyedge, searchpoint);
+    if (rightofchild) {
+      otricopy(child->keyedge, *searchtri);
+      grandchild = splay(m, child->rchild, searchpoint, searchtri);
+      child->rchild = grandchild;
+    } else {
+      grandchild = splay(m, child->lchild, searchpoint, searchtri);
+      child->lchild = grandchild;
+    }
+    if (grandchild == (struct splaynode *) NULL) {
+      if (rightofroot) {
+        splaytree->rchild = child->lchild;
+        child->lchild = splaytree;
+      } else {
+        splaytree->lchild = child->rchild;
+        child->rchild = splaytree;
+      }
+      return child;
+    }
+    if (rightofchild) {
+      if (rightofroot) {
+        splaytree->rchild = child->lchild;
+        child->lchild = splaytree;
+      } else {
+        splaytree->lchild = grandchild->rchild;
+        grandchild->rchild = splaytree;
+      }
+      child->rchild = grandchild->lchild;
+      grandchild->lchild = child;
+    } else {
+      if (rightofroot) {
+        splaytree->rchild = grandchild->lchild;
+        grandchild->lchild = splaytree;
+      } else {
+        splaytree->lchild = child->rchild;
+        child->rchild = splaytree;
+      }
+      child->lchild = grandchild->rchild;
+      grandchild->rchild = child;
+    }
+    return grandchild;
+  } else {
+    lefttree = splay(m, splaytree->lchild, searchpoint, searchtri);
+    righttree = splay(m, splaytree->rchild, searchpoint, searchtri);
+
+    pooldealloc(&m->splaynodes, (VOID *) splaytree);
+    if (lefttree == (struct splaynode *) NULL) {
+      return righttree;
+    } else if (righttree == (struct splaynode *) NULL) {
+      return lefttree;
+    } else if (lefttree->rchild == (struct splaynode *) NULL) {
+      lefttree->rchild = righttree->lchild;
+      righttree->lchild = lefttree;
+      return righttree;
+    } else if (righttree->lchild == (struct splaynode *) NULL) {
+      righttree->lchild = lefttree->rchild;
+      lefttree->rchild = righttree;
+      return lefttree;
+    } else {
+/*      printf("Holy Toledo!!!\n"); */
+      leftright = lefttree->rchild;
+      while (leftright->rchild != (struct splaynode *) NULL) {
+        leftright = leftright->rchild;
+      }
+      leftright->rchild = righttree;
+      return lefttree;
+    }
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *splayinsert(struct mesh *m, struct splaynode *splayroot,
+                              struct otri *newkey, vertex searchpoint)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *splayinsert(m, splayroot, newkey, searchpoint)
+struct mesh *m;
+struct splaynode *splayroot;
+struct otri *newkey;
+vertex searchpoint;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct splaynode *newsplaynode;
+
+  newsplaynode = (struct splaynode *) poolalloc(&m->splaynodes);
+  otricopy(*newkey, newsplaynode->keyedge);
+  dest(*newkey, newsplaynode->keydest);
+  if (splayroot == (struct splaynode *) NULL) {
+    newsplaynode->lchild = (struct splaynode *) NULL;
+    newsplaynode->rchild = (struct splaynode *) NULL;
+  } else if (rightofhyperbola(m, &splayroot->keyedge, searchpoint)) {
+    newsplaynode->lchild = splayroot;
+    newsplaynode->rchild = splayroot->rchild;
+    splayroot->rchild = (struct splaynode *) NULL;
+  } else {
+    newsplaynode->lchild = splayroot->lchild;
+    newsplaynode->rchild = splayroot;
+    splayroot->lchild = (struct splaynode *) NULL;
+  }
+  return newsplaynode;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *circletopinsert(struct mesh *m, struct behavior *b,
+                                  struct splaynode *splayroot,
+                                  struct otri *newkey,
+                                  vertex pa, vertex pb, vertex pc, REAL topy)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *circletopinsert(m, b, splayroot, newkey, pa, pb, pc, topy)
+struct mesh *m;
+struct behavior *b;
+struct splaynode *splayroot;
+struct otri *newkey;
+vertex pa;
+vertex pb;
+vertex pc;
+REAL topy;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL ccwabc;
+  REAL xac, yac, xbc, ybc;
+  REAL aclen2, bclen2;
+  REAL searchpoint[2];
+  struct otri dummytri;
+
+  ccwabc = counterclockwise(m, b, pa, pb, pc);
+  xac = pa[0] - pc[0];
+  yac = pa[1] - pc[1];
+  xbc = pb[0] - pc[0];
+  ybc = pb[1] - pc[1];
+  aclen2 = xac * xac + yac * yac;
+  bclen2 = xbc * xbc + ybc * ybc;
+  searchpoint[0] = pc[0] - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
+  searchpoint[1] = topy;
+  return splayinsert(m, splay(m, splayroot, (vertex) searchpoint, &dummytri),
+                     newkey, (vertex) searchpoint);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *frontlocate(struct mesh *m, struct splaynode *splayroot,
+                              struct otri *bottommost, vertex searchvertex,
+                              struct otri *searchtri, int *farright)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *frontlocate(m, splayroot, bottommost, searchvertex,
+                              searchtri, farright)
+struct mesh *m;
+struct splaynode *splayroot;
+struct otri *bottommost;
+vertex searchvertex;
+struct otri *searchtri;
+int *farright;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int farrightflag;
+  triangle ptr;                       /* Temporary variable used by onext(). */
+
+  otricopy(*bottommost, *searchtri);
+  splayroot = splay(m, splayroot, searchvertex, searchtri);
+
+  farrightflag = 0;
+  while (!farrightflag && rightofhyperbola(m, searchtri, searchvertex)) {
+    onextself(*searchtri);
+    farrightflag = otriequal(*searchtri, *bottommost);
+  }
+  *farright = farrightflag;
+  return splayroot;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long sweeplinedelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long sweeplinedelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event **eventheap;
+  struct event *events;
+  struct event *freeevents;
+  struct event *nextevent;
+  struct event *newevent;
+  struct splaynode *splayroot;
+  struct otri bottommost;
+  struct otri searchtri;
+  struct otri fliptri;
+  struct otri lefttri, righttri, farlefttri, farrighttri;
+  struct otri inserttri;
+  vertex firstvertex, secondvertex;
+  vertex nextvertex, lastvertex;
+  vertex connectvertex;
+  vertex leftvertex, midvertex, rightvertex;
+  REAL lefttest, righttest;
+  int heapsize;
+  int check4events, farrightflag;
+  triangle ptr;   /* Temporary variable used by sym(), onext(), and oprev(). */
+
+  poolinit(&m->splaynodes, sizeof(struct splaynode), SPLAYNODEPERBLOCK,
+           SPLAYNODEPERBLOCK, POINTER, 0);
+  splayroot = (struct splaynode *) NULL;
+
+  if (b->verbose) {
+    printf("  Placing vertices in event heap.\n");
+  }
+  createeventheap(m, &eventheap, &events, &freeevents);
+  heapsize = m->invertices;
+
+  if (b->verbose) {
+    printf("  Forming triangulation.\n");
+  }
+  maketriangle(m, b, &lefttri);
+  maketriangle(m, b, &righttri);
+  bond(lefttri, righttri);
+  lnextself(lefttri);
+  lprevself(righttri);
+  bond(lefttri, righttri);
+  lnextself(lefttri);
+  lprevself(righttri);
+  bond(lefttri, righttri);
+  firstvertex = (vertex) eventheap[0]->eventptr;
+  eventheap[0]->eventptr = (VOID *) freeevents;
+  freeevents = eventheap[0];
+  eventheapdelete(eventheap, heapsize, 0);
+  heapsize--;
+  do {
+    if (heapsize == 0) {
+      printf("Error:  Input vertices are all identical.\n");
+      exit(1);
+    }
+    secondvertex = (vertex) eventheap[0]->eventptr;
+    eventheap[0]->eventptr = (VOID *) freeevents;
+    freeevents = eventheap[0];
+    eventheapdelete(eventheap, heapsize, 0);
+    heapsize--;
+    if ((firstvertex[0] == secondvertex[0]) &&
+        (firstvertex[1] == secondvertex[1])) {
+      if (!b->quiet) {
+        printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+               secondvertex[0], secondvertex[1]);
+      }
+      setvertextype(secondvertex, UNDEADVERTEX);
+      m->undeads++;
+    }
+  } while ((firstvertex[0] == secondvertex[0]) &&
+           (firstvertex[1] == secondvertex[1]));
+  setorg(lefttri, firstvertex);
+  setdest(lefttri, secondvertex);
+  setorg(righttri, secondvertex);
+  setdest(righttri, firstvertex);
+  lprev(lefttri, bottommost);
+  lastvertex = secondvertex;
+  while (heapsize > 0) {
+    nextevent = eventheap[0];
+    eventheapdelete(eventheap, heapsize, 0);
+    heapsize--;
+    check4events = 1;
+    if (nextevent->xkey < m->xmin) {
+      decode(nextevent->eventptr, fliptri);
+      oprev(fliptri, farlefttri);
+      check4deadevent(&farlefttri, &freeevents, eventheap, &heapsize);
+      onext(fliptri, farrighttri);
+      check4deadevent(&farrighttri, &freeevents, eventheap, &heapsize);
+
+      if (otriequal(farlefttri, bottommost)) {
+        lprev(fliptri, bottommost);
+      }
+      flip(m, b, &fliptri);
+      setapex(fliptri, NULL);
+      lprev(fliptri, lefttri);
+      lnext(fliptri, righttri);
+      sym(lefttri, farlefttri);
+
+      if (randomnation(SAMPLERATE) == 0) {
+        symself(fliptri);
+        dest(fliptri, leftvertex);
+        apex(fliptri, midvertex);
+        org(fliptri, rightvertex);
+        splayroot = circletopinsert(m, b, splayroot, &lefttri, leftvertex,
+                                    midvertex, rightvertex, nextevent->ykey);
+      }
+    } else {
+      nextvertex = (vertex) nextevent->eventptr;
+      if ((nextvertex[0] == lastvertex[0]) &&
+          (nextvertex[1] == lastvertex[1])) {
+        if (!b->quiet) {
+          printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+                 nextvertex[0], nextvertex[1]);
+        }
+        setvertextype(nextvertex, UNDEADVERTEX);
+        m->undeads++;
+        check4events = 0;
+      } else {
+        lastvertex = nextvertex;
+
+        splayroot = frontlocate(m, splayroot, &bottommost, nextvertex,
+                                &searchtri, &farrightflag);
+/*
+        otricopy(bottommost, searchtri);
+        farrightflag = 0;
+        while (!farrightflag && rightofhyperbola(m, &searchtri, nextvertex)) {
+          onextself(searchtri);
+          farrightflag = otriequal(searchtri, bottommost);
+        }
+*/
+
+        check4deadevent(&searchtri, &freeevents, eventheap, &heapsize);
+
+        otricopy(searchtri, farrighttri);
+        sym(searchtri, farlefttri);
+        maketriangle(m, b, &lefttri);
+        maketriangle(m, b, &righttri);
+        dest(farrighttri, connectvertex);
+        setorg(lefttri, connectvertex);
+        setdest(lefttri, nextvertex);
+        setorg(righttri, nextvertex);
+        setdest(righttri, connectvertex);
+        bond(lefttri, righttri);
+        lnextself(lefttri);
+        lprevself(righttri);
+        bond(lefttri, righttri);
+        lnextself(lefttri);
+        lprevself(righttri);
+        bond(lefttri, farlefttri);
+        bond(righttri, farrighttri);
+        if (!farrightflag && otriequal(farrighttri, bottommost)) {
+          otricopy(lefttri, bottommost);
+        }
+
+        if (randomnation(SAMPLERATE) == 0) {
+          splayroot = splayinsert(m, splayroot, &lefttri, nextvertex);
+        } else if (randomnation(SAMPLERATE) == 0) {
+          lnext(righttri, inserttri);
+          splayroot = splayinsert(m, splayroot, &inserttri, nextvertex);
+        }
+      }
+    }
+    nextevent->eventptr = (VOID *) freeevents;
+    freeevents = nextevent;
+
+    if (check4events) {
+      apex(farlefttri, leftvertex);
+      dest(lefttri, midvertex);
+      apex(lefttri, rightvertex);
+      lefttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
+      if (lefttest > 0.0) {
+        newevent = freeevents;
+        freeevents = (struct event *) freeevents->eventptr;
+        newevent->xkey = m->xminextreme;
+        newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
+                                   lefttest);
+        newevent->eventptr = (VOID *) encode(lefttri);
+        eventheapinsert(eventheap, heapsize, newevent);
+        heapsize++;
+        setorg(lefttri, newevent);
+      }
+      apex(righttri, leftvertex);
+      org(righttri, midvertex);
+      apex(farrighttri, rightvertex);
+      righttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
+      if (righttest > 0.0) {
+        newevent = freeevents;
+        freeevents = (struct event *) freeevents->eventptr;
+        newevent->xkey = m->xminextreme;
+        newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
+                                   righttest);
+        newevent->eventptr = (VOID *) encode(farrighttri);
+        eventheapinsert(eventheap, heapsize, newevent);
+        heapsize++;
+        setorg(farrighttri, newevent);
+      }
+    }
+  }
+
+  pooldeinit(&m->splaynodes);
+  lprevself(bottommost);
+  return removeghosts(m, b, &bottommost);
+}
+
+#endif /* not REDUCED */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Sweepline Delaunay triangulation ends here                *********/
+
+/********* General mesh construction routines begin here             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  delaunay()   Form a Delaunay triangulation.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+long delaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long delaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  long hulledges;
+
+  m->eextras = 0;
+  initializetrisubpools(m, b);
+
+#ifdef REDUCED
+  if (!b->quiet) {
+    printf(
+      "Constructing Delaunay triangulation by divide-and-conquer method.\n");
+  }
+  hulledges = divconqdelaunay(m, b);
+#else /* not REDUCED */
+  if (!b->quiet) {
+    printf("Constructing Delaunay triangulation ");
+    if (b->incremental) {
+      printf("by incremental method.\n");
+    } else if (b->sweepline) {
+      printf("by sweepline method.\n");
+    } else {
+      printf("by divide-and-conquer method.\n");
+    }
+  }
+  if (b->incremental) {
+    hulledges = incrementaldelaunay(m, b);
+  } else if (b->sweepline) {
+    hulledges = sweeplinedelaunay(m, b);
+  } else {
+    hulledges = divconqdelaunay(m, b);
+  }
+#endif /* not REDUCED */
+
+  if (m->triangles.items == 0) {
+    /* The input vertices were all collinear, so there are no triangles. */
+    return 0l;
+  } else {
+    return hulledges;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  reconstruct()   Reconstruct a triangulation from its .ele (and possibly  */
+/*                  .poly) file.  Used when the -r switch is used.           */
+/*                                                                           */
+/*  Reads an .ele file and reconstructs the original mesh.  If the -p switch */
+/*  is used, this procedure will also read a .poly file and reconstruct the  */
+/*  subsegments of the original mesh.  If the -a switch is used, this        */
+/*  procedure will also read an .area file and set a maximum area constraint */
+/*  on each triangle.                                                        */
+/*                                                                           */
+/*  Vertices that are not corners of triangles, such as nodes on edges of    */
+/*  subparametric elements, are discarded.                                   */
+/*                                                                           */
+/*  This routine finds the adjacencies between triangles (and subsegments)   */
+/*  by forming one stack of triangles for each vertex.  Each triangle is on  */
+/*  three different stacks simultaneously.  Each triangle's subsegment       */
+/*  pointers are used to link the items in each stack.  This memory-saving   */
+/*  feature makes the code harder to read.  The most important thing to keep */
+/*  in mind is that each triangle is removed from a stack precisely when     */
+/*  the corresponding pointer is adjusted to refer to a subsegment rather    */
+/*  than the next triangle of the stack.                                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+int reconstruct(struct mesh *m, struct behavior *b, int *trianglelist,
+                REAL *triangleattriblist, REAL *trianglearealist,
+                int elements, int corners, int attribs,
+                int *segmentlist,int *segmentmarkerlist, int numberofsegments)
+#else /* not ANSI_DECLARATORS */
+int reconstruct(m, b, trianglelist, triangleattriblist, trianglearealist,
+                elements, corners, attribs, segmentlist, segmentmarkerlist,
+                numberofsegments)
+struct mesh *m;
+struct behavior *b;
+int *trianglelist;
+REAL *triangleattriblist;
+REAL *trianglearealist;
+int elements;
+int corners;
+int attribs;
+int *segmentlist;
+int *segmentmarkerlist;
+int numberofsegments;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+long reconstruct(struct mesh *m, struct behavior *b, char *elefilename,
+                 char *areafilename, char *polyfilename, FILE *polyfile)
+#else /* not ANSI_DECLARATORS */
+long reconstruct(m, b, elefilename, areafilename, polyfilename, polyfile)
+struct mesh *m;
+struct behavior *b;
+char *elefilename;
+char *areafilename;
+char *polyfilename;
+FILE *polyfile;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int vertexindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *elefile;
+  FILE *areafile;
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  int areaelements;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop;
+  struct otri triangleleft;
+  struct otri checktri;
+  struct otri checkleft;
+  struct otri checkneighbor;
+  struct osub subsegloop;
+  triangle *vertexarray;
+  triangle *prevlink;
+  triangle nexttri;
+  vertex tdest, tapex;
+  vertex checkdest, checkapex;
+  vertex shorg;
+  vertex killvertex;
+  REAL area;
+  int corner[3];
+  int end[2];
+  int killvertexindex;
+  int incorners;
+  int segmentmarkers;
+  int boundmarker;
+  int aroundvertex;
+  long hullsize;
+  int notfound;
+  long elementnumber, segmentnumber;
+  int i, j;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+  m->inelements = elements;
+  incorners = corners;
+  if (incorners < 3) {
+    printf("Error:  Triangles must have at least 3 vertices.\n");
+    exit(1);
+  }
+  m->eextras = attribs;
+#else /* not TRILIBRARY */
+  /* Read the triangles from an .ele file. */
+  if (!b->quiet) {
+    printf("Opening %s.\n", elefilename);
+  }
+  elefile = fopen(elefilename, "r");
+  if (elefile == (FILE *) NULL) {
+    printf("  Error:  Cannot access file %s.\n", elefilename);
+    exit(1);
+  }
+  /* Read number of triangles, number of vertices per triangle, and */
+  /*   number of triangle attributes from .ele file.                */
+  stringptr = readline(inputline, elefile, elefilename);
+  m->inelements = (int) strtol(stringptr, &stringptr, 0);
+  stringptr = findfield(stringptr);
+  if (*stringptr == '\0') {
+    incorners = 3;
+  } else {
+    incorners = (int) strtol(stringptr, &stringptr, 0);
+    if (incorners < 3) {
+      printf("Error:  Triangles in %s must have at least 3 vertices.\n",
+             elefilename);
+      exit(1);
+    }
+  }
+  stringptr = findfield(stringptr);
+  if (*stringptr == '\0') {
+    m->eextras = 0;
+  } else {
+    m->eextras = (int) strtol(stringptr, &stringptr, 0);
+  }
+#endif /* not TRILIBRARY */
+
+  initializetrisubpools(m, b);
+
+  /* Create the triangles. */
+  for (elementnumber = 1; elementnumber <= m->inelements; elementnumber++) {
+    maketriangle(m, b, &triangleloop);
+    /* Mark the triangle as living. */
+    triangleloop.tri[3] = (triangle) triangleloop.tri;
+  }
+
+  if (b->poly) {
+#ifdef TRILIBRARY
+    m->insegments = numberofsegments;
+    segmentmarkers = segmentmarkerlist != (int *) NULL;
+#else /* not TRILIBRARY */
+    /* Read number of segments and number of segment */
+    /*   boundary markers from .poly file.           */
+    stringptr = readline(inputline, polyfile, b->inpolyfilename);
+    m->insegments = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      segmentmarkers = 0;
+    } else {
+      segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+#endif /* not TRILIBRARY */
+
+    /* Create the subsegments. */
+    for (segmentnumber = 1; segmentnumber <= m->insegments; segmentnumber++) {
+      makesubseg(m, &subsegloop);
+      /* Mark the subsegment as living. */
+      subsegloop.ss[2] = (subseg) subsegloop.ss;
+    }
+  }
+
+#ifdef TRILIBRARY
+  vertexindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (b->vararea) {
+    /* Open an .area file, check for consistency with the .ele file. */
+    if (!b->quiet) {
+      printf("Opening %s.\n", areafilename);
+    }
+    areafile = fopen(areafilename, "r");
+    if (areafile == (FILE *) NULL) {
+      printf("  Error:  Cannot access file %s.\n", areafilename);
+      exit(1);
+    }
+    stringptr = readline(inputline, areafile, areafilename);
+    areaelements = (int) strtol(stringptr, &stringptr, 0);
+    if (areaelements != m->inelements) {
+      printf("Error:  %s and %s disagree on number of triangles.\n",
+             elefilename, areafilename);
+      exit(1);
+    }
+  }
+#endif /* not TRILIBRARY */
+
+  if (!b->quiet) {
+    printf("Reconstructing mesh.\n");
+  }
+  /* Allocate a temporary array that maps each vertex to some adjacent */
+  /*   triangle.  I took care to allocate all the permanent memory for */
+  /*   triangles and subsegments first.                                */
+  vertexarray = (triangle *)
+                trimalloc(m->vertices.items * (int) sizeof(triangle));
+  /* Each vertex is initially unrepresented. */
+  for (i = 0; i < m->vertices.items; i++) {
+    vertexarray[i] = (triangle) m->dummytri;
+  }
+
+  if (b->verbose) {
+    printf("  Assembling triangles.\n");
+  }
+  /* Read the triangles from the .ele file, and link */
+  /*   together those that share an edge.            */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+#ifdef TRILIBRARY
+    /* Copy the triangle's three corners. */
+    for (j = 0; j < 3; j++) {
+      corner[j] = trianglelist[vertexindex++];
+      if ((corner[j] < b->firstnumber) ||
+          (corner[j] >= b->firstnumber + m->invertices)) {
+        printf("Error:  Triangle %ld has an invalid vertex index.\n",
+               elementnumber);
+        exit(1);
+      }
+    }
+#else /* not TRILIBRARY */
+    /* Read triangle number and the triangle's three corners. */
+    stringptr = readline(inputline, elefile, elefilename);
+    for (j = 0; j < 3; j++) {
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Triangle %ld is missing vertex %d in %s.\n",
+               elementnumber, j + 1, elefilename);
+        exit(1);
+      } else {
+        corner[j] = (int) strtol(stringptr, &stringptr, 0);
+        if ((corner[j] < b->firstnumber) ||
+            (corner[j] >= b->firstnumber + m->invertices)) {
+          printf("Error:  Triangle %ld has an invalid vertex index.\n",
+                 elementnumber);
+          exit(1);
+        }
+      }
+    }
+#endif /* not TRILIBRARY */
+
+    /* Find out about (and throw away) extra nodes. */
+    for (j = 3; j < incorners; j++) {
+#ifdef TRILIBRARY
+      killvertexindex = trianglelist[vertexindex++];
+#else /* not TRILIBRARY */
+      stringptr = findfield(stringptr);
+      if (*stringptr != '\0') {
+        killvertexindex = (int) strtol(stringptr, &stringptr, 0);
+#endif /* not TRILIBRARY */
+        if ((killvertexindex >= b->firstnumber) &&
+            (killvertexindex < b->firstnumber + m->invertices)) {
+          /* Delete the non-corner vertex if it's not already deleted. */
+          killvertex = getvertex(m, b, killvertexindex);
+          if (vertextype(killvertex) != DEADVERTEX) {
+            vertexdealloc(m, killvertex);
+          }
+        }
+#ifndef TRILIBRARY
+      }
+#endif /* not TRILIBRARY */
+    }
+
+    /* Read the triangle's attributes. */
+    for (j = 0; j < m->eextras; j++) {
+#ifdef TRILIBRARY
+      setelemattribute(triangleloop, j, triangleattriblist[attribindex++]);
+#else /* not TRILIBRARY */
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        setelemattribute(triangleloop, j, 0);
+      } else {
+        setelemattribute(triangleloop, j,
+                         (REAL) strtod(stringptr, &stringptr));
+      }
+#endif /* not TRILIBRARY */
+    }
+
+    if (b->vararea) {
+#ifdef TRILIBRARY
+      area = trianglearealist[elementnumber - b->firstnumber];
+#else /* not TRILIBRARY */
+      /* Read an area constraint from the .area file. */
+      stringptr = readline(inputline, areafile, areafilename);
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        area = -1.0;                      /* No constraint on this triangle. */
+      } else {
+        area = (REAL) strtod(stringptr, &stringptr);
+      }
+#endif /* not TRILIBRARY */
+      setareabound(triangleloop, area);
+    }
+
+    /* Set the triangle's vertices. */
+    triangleloop.orient = 0;
+    setorg(triangleloop, getvertex(m, b, corner[0]));
+    setdest(triangleloop, getvertex(m, b, corner[1]));
+    setapex(triangleloop, getvertex(m, b, corner[2]));
+    /* Try linking the triangle to others that share these vertices. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      /* Take the number for the origin of triangleloop. */
+      aroundvertex = corner[triangleloop.orient];
+      /* Look for other triangles having this vertex. */
+      nexttri = vertexarray[aroundvertex - b->firstnumber];
+      /* Link the current triangle to the next one in the stack. */
+      triangleloop.tri[6 + triangleloop.orient] = nexttri;
+      /* Push the current triangle onto the stack. */
+      vertexarray[aroundvertex - b->firstnumber] = encode(triangleloop);
+      decode(nexttri, checktri);
+      if (checktri.tri != m->dummytri) {
+        dest(triangleloop, tdest);
+        apex(triangleloop, tapex);
+        /* Look for other triangles that share an edge. */
+        do {
+          dest(checktri, checkdest);
+          apex(checktri, checkapex);
+          if (tapex == checkdest) {
+            /* The two triangles share an edge; bond them together. */
+            lprev(triangleloop, triangleleft);
+            bond(triangleleft, checktri);
+          }
+          if (tdest == checkapex) {
+            /* The two triangles share an edge; bond them together. */
+            lprev(checktri, checkleft);
+            bond(triangleloop, checkleft);
+          }
+          /* Find the next triangle in the stack. */
+          nexttri = checktri.tri[6 + checktri.orient];
+          decode(nexttri, checktri);
+        } while (checktri.tri != m->dummytri);
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+
+#ifdef TRILIBRARY
+  vertexindex = 0;
+#else /* not TRILIBRARY */
+  fclose(elefile);
+  if (b->vararea) {
+    fclose(areafile);
+  }
+#endif /* not TRILIBRARY */
+
+  hullsize = 0;                      /* Prepare to count the boundary edges. */
+  if (b->poly) {
+    if (b->verbose) {
+      printf("  Marking segments in triangulation.\n");
+    }
+    /* Read the segments from the .poly file, and link them */
+    /*   to their neighboring triangles.                    */
+    boundmarker = 0;
+    traversalinit(&m->subsegs);
+    subsegloop.ss = subsegtraverse(m);
+    segmentnumber = b->firstnumber;
+    while (subsegloop.ss != (subseg *) NULL) {
+#ifdef TRILIBRARY
+      end[0] = segmentlist[vertexindex++];
+      end[1] = segmentlist[vertexindex++];
+      if (segmentmarkers) {
+        boundmarker = segmentmarkerlist[segmentnumber - b->firstnumber];
+      }
+#else /* not TRILIBRARY */
+      /* Read the endpoints of each segment, and possibly a boundary marker. */
+      stringptr = readline(inputline, polyfile, b->inpolyfilename);
+      /* Skip the first (segment number) field. */
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %ld has no endpoints in %s.\n", segmentnumber,
+               polyfilename);
+        exit(1);
+      } else {
+        end[0] = (int) strtol(stringptr, &stringptr, 0);
+      }
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %ld is missing its second endpoint in %s.\n",
+               segmentnumber, polyfilename);
+        exit(1);
+      } else {
+        end[1] = (int) strtol(stringptr, &stringptr, 0);
+      }
+      if (segmentmarkers) {
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          boundmarker = 0;
+        } else {
+          boundmarker = (int) strtol(stringptr, &stringptr, 0);
+        }
+      }
+#endif /* not TRILIBRARY */
+      for (j = 0; j < 2; j++) {
+        if ((end[j] < b->firstnumber) ||
+            (end[j] >= b->firstnumber + m->invertices)) {
+          printf("Error:  Segment %ld has an invalid vertex index.\n", 
+                 segmentnumber);
+          exit(1);
+        }
+      }
+
+      /* set the subsegment's vertices. */
+      subsegloop.ssorient = 0;
+      setsorg(subsegloop, getvertex(m, b, end[0]));
+      setsdest(subsegloop, getvertex(m, b, end[1]));
+      setmark(subsegloop, boundmarker);
+      /* Try linking the subsegment to triangles that share these vertices. */
+      for (subsegloop.ssorient = 0; subsegloop.ssorient < 2;
+           subsegloop.ssorient++) {
+        /* Take the number for the destination of subsegloop. */
+        aroundvertex = end[1 - subsegloop.ssorient];
+        /* Look for triangles having this vertex. */
+        prevlink = &vertexarray[aroundvertex - b->firstnumber];
+        nexttri = vertexarray[aroundvertex - b->firstnumber];
+        decode(nexttri, checktri);
+        sorg(subsegloop, shorg);
+        notfound = 1;
+        /* Look for triangles having this edge.  Note that I'm only       */
+        /*   comparing each triangle's destination with the subsegment;   */
+        /*   each triangle's apex is handled through a different vertex.  */
+        /*   Because each triangle appears on three vertices' lists, each */
+        /*   occurrence of a triangle on a list can (and does) represent  */
+        /*   an edge.  In this way, most edges are represented twice, and */
+        /*   every triangle-subsegment bond is represented once.          */
+        while (notfound && (checktri.tri != m->dummytri)) {
+          dest(checktri, checkdest);
+          if (shorg == checkdest) {
+            /* We have a match.  Remove this triangle from the list. */
+            *prevlink = checktri.tri[6 + checktri.orient];
+            /* Bond the subsegment to the triangle. */
+            tsbond(checktri, subsegloop);
+            /* Check if this is a boundary edge. */
+            sym(checktri, checkneighbor);
+            if (checkneighbor.tri == m->dummytri) {
+              /* The next line doesn't insert a subsegment (because there's */
+              /*   already one there), but it sets the boundary markers of  */
+              /*   the existing subsegment and its vertices.                */
+              insertsubseg(m, b, &checktri, 1);
+              hullsize++;
+            }
+            notfound = 0;
+          }
+          /* Find the next triangle in the stack. */
+          prevlink = &checktri.tri[6 + checktri.orient];
+          nexttri = checktri.tri[6 + checktri.orient];
+          decode(nexttri, checktri);
+        }
+      }
+      subsegloop.ss = subsegtraverse(m);
+      segmentnumber++;
+    }
+  }
+
+  /* Mark the remaining edges as not being attached to any subsegment. */
+  /* Also, count the (yet uncounted) boundary edges.                   */
+  for (i = 0; i < m->vertices.items; i++) {
+    /* Search the stack of triangles adjacent to a vertex. */
+    nexttri = vertexarray[i];
+    decode(nexttri, checktri);
+    while (checktri.tri != m->dummytri) {
+      /* Find the next triangle in the stack before this */
+      /*   information gets overwritten.                 */
+      nexttri = checktri.tri[6 + checktri.orient];
+      /* No adjacent subsegment.  (This overwrites the stack info.) */
+      tsdissolve(checktri);
+      sym(checktri, checkneighbor);
+      if (checkneighbor.tri == m->dummytri) {
+        insertsubseg(m, b, &checktri, 1);
+        hullsize++;
+      }
+      decode(nexttri, checktri);
+    }
+  }
+
+  trifree((VOID *) vertexarray);
+  return hullsize;
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* General mesh construction routines end here               *********/
+
+/********* Segment insertion begins here                             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  finddirection()   Find the first triangle on the path from one point     */
+/*                    to another.                                            */
+/*                                                                           */
+/*  Finds the triangle that intersects a line segment drawn from the         */
+/*  origin of `searchtri' to the point `searchpoint', and returns the result */
+/*  in `searchtri'.  The origin of `searchtri' does not change, even though  */
+/*  the triangle returned may differ from the one passed in.  This routine   */
+/*  is used to find the direction to move in to get from one point to        */
+/*  another.                                                                 */
+/*                                                                           */
+/*  The return value notes whether the destination or apex of the found      */
+/*  triangle is collinear with the two points in question.                   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum finddirectionresult finddirection(struct mesh *m, struct behavior *b,
+                                       struct otri *searchtri,
+                                       vertex searchpoint)
+#else /* not ANSI_DECLARATORS */
+enum finddirectionresult finddirection(m, b, searchtri, searchpoint)
+struct mesh *m;
+struct behavior *b;
+struct otri *searchtri;
+vertex searchpoint;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri checktri;
+  vertex startvertex;
+  vertex leftvertex, rightvertex;
+  REAL leftccw, rightccw;
+  int leftflag, rightflag;
+  triangle ptr;           /* Temporary variable used by onext() and oprev(). */
+
+  org(*searchtri, startvertex);
+  dest(*searchtri, rightvertex);
+  apex(*searchtri, leftvertex);
+  /* Is `searchpoint' to the left? */
+  leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
+  leftflag = leftccw > 0.0;
+  /* Is `searchpoint' to the right? */
+  rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
+  rightflag = rightccw > 0.0;
+  if (leftflag && rightflag) {
+    /* `searchtri' faces directly away from `searchpoint'.  We could go left */
+    /*   or right.  Ask whether it's a triangle or a boundary on the left.   */
+    onext(*searchtri, checktri);
+    if (checktri.tri == m->dummytri) {
+      leftflag = 0;
+    } else {
+      rightflag = 0;
+    }
+  }
+  while (leftflag) {
+    /* Turn left until satisfied. */
+    onextself(*searchtri);
+    if (searchtri->tri == m->dummytri) {
+      printf("Internal error in finddirection():  Unable to find a\n");
+      printf("  triangle leading from (%.12g, %.12g) to", startvertex[0],
+             startvertex[1]);
+      printf("  (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
+      internalerror();
+    }
+    apex(*searchtri, leftvertex);
+    rightccw = leftccw;
+    leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
+    leftflag = leftccw > 0.0;
+  }
+  while (rightflag) {
+    /* Turn right until satisfied. */
+    oprevself(*searchtri);
+    if (searchtri->tri == m->dummytri) {
+      printf("Internal error in finddirection():  Unable to find a\n");
+      printf("  triangle leading from (%.12g, %.12g) to", startvertex[0],
+             startvertex[1]);
+      printf("  (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
+      internalerror();
+    }
+    dest(*searchtri, rightvertex);
+    leftccw = rightccw;
+    rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
+    rightflag = rightccw > 0.0;
+  }
+  if (leftccw == 0.0) {
+    return LEFTCOLLINEAR;
+  } else if (rightccw == 0.0) {
+    return RIGHTCOLLINEAR;
+  } else {
+    return WITHIN;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  segmentintersection()   Find the intersection of an existing segment     */
+/*                          and a segment that is being inserted.  Insert    */
+/*                          a vertex at the intersection, splitting an       */
+/*                          existing subsegment.                             */
+/*                                                                           */
+/*  The segment being inserted connects the apex of splittri to endpoint2.   */
+/*  splitsubseg is the subsegment being split, and MUST adjoin splittri.     */
+/*  Hence, endpoints of the subsegment being split are the origin and        */
+/*  destination of splittri.                                                 */
+/*                                                                           */
+/*  On completion, splittri is a handle having the newly inserted            */
+/*  intersection point as its origin, and endpoint1 as its destination.      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void segmentintersection(struct mesh *m, struct behavior *b,
+                         struct otri *splittri, struct osub *splitsubseg,
+                         vertex endpoint2)
+#else /* not ANSI_DECLARATORS */
+void segmentintersection(m, b, splittri, splitsubseg, endpoint2)
+struct mesh *m;
+struct behavior *b;
+struct otri *splittri;
+struct osub *splitsubseg;
+vertex endpoint2;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex endpoint1;
+  vertex torg, tdest;
+  vertex leftvertex, rightvertex;
+  vertex newvertex;
+  enum insertvertexresult success;
+  enum finddirectionresult collinear;
+  REAL ex, ey;
+  REAL tx, ty;
+  REAL etx, ety;
+  REAL split, denom;
+  int i;
+  triangle ptr;                       /* Temporary variable used by onext(). */
+
+  /* Find the other three segment endpoints. */
+  apex(*splittri, endpoint1);
+  org(*splittri, torg);
+  dest(*splittri, tdest);
+  /* Segment intersection formulae; see the Antonio reference. */
+  tx = tdest[0] - torg[0];
+  ty = tdest[1] - torg[1];
+  ex = endpoint2[0] - endpoint1[0];
+  ey = endpoint2[1] - endpoint1[1];
+  etx = torg[0] - endpoint2[0];
+  ety = torg[1] - endpoint2[1];
+  denom = ty * ex - tx * ey;
+  if (denom == 0.0) {
+    printf("Internal error in segmentintersection():");
+    printf("  Attempt to find intersection of parallel segments.\n");
+    internalerror();
+  }
+  split = (ey * etx - ex * ety) / denom;
+  /* Create the new vertex. */
+  newvertex = (vertex) poolalloc(&m->vertices);
+  /* Interpolate its coordinate and attributes. */
+  for (i = 0; i < 2 + m->nextras; i++) {
+    newvertex[i] = torg[i] + split * (tdest[i] - torg[i]);
+  }
+  setvertexmark(newvertex, mark(*splitsubseg));
+  setvertextype(newvertex, INPUTVERTEX);
+  if (b->verbose > 1) {
+    printf(
+  "  Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
+           torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]);
+  }
+  /* Insert the intersection vertex.  This should always succeed. */
+  success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0, 0.0);
+  if (success != SUCCESSFULVERTEX) {
+    printf("Internal error in segmentintersection():\n");
+    printf("  Failure to split a segment.\n");
+    internalerror();
+  }
+  if (m->steinerleft > 0) {
+    m->steinerleft--;
+  }
+  /* Inserting the vertex may have caused edge flips.  We wish to rediscover */
+  /*   the edge connecting endpoint1 to the new intersection vertex.         */
+  collinear = finddirection(m, b, splittri, endpoint1);
+  dest(*splittri, rightvertex);
+  apex(*splittri, leftvertex);
+  if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) {
+    onextself(*splittri);
+  } else if ((rightvertex[0] != endpoint1[0]) ||
+             (rightvertex[1] != endpoint1[1])) {
+    printf("Internal error in segmentintersection():\n");
+    printf("  Topological inconsistency after splitting a segment.\n");
+    internalerror();
+  }
+  /* `splittri' should have destination endpoint1. */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scoutsegment()   Scout the first triangle on the path from one endpoint  */
+/*                   to another, and check for completion (reaching the      */
+/*                   second endpoint), a collinear vertex, or the            */
+/*                   intersection of two segments.                           */
+/*                                                                           */
+/*  Returns one if the entire segment is successfully inserted, and zero if  */
+/*  the job must be finished by conformingedge() or constrainededge().       */
+/*                                                                           */
+/*  If the first triangle on the path has the second endpoint as its         */
+/*  destination or apex, a subsegment is inserted and the job is done.       */
+/*                                                                           */
+/*  If the first triangle on the path has a destination or apex that lies on */
+/*  the segment, a subsegment is inserted connecting the first endpoint to   */
+/*  the collinear vertex, and the search is continued from the collinear     */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*  If the first triangle on the path has a subsegment opposite its origin,  */
+/*  then there is a segment that intersects the segment being inserted.      */
+/*  Their intersection vertex is inserted, splitting the subsegment.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri,
+                 vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+int scoutsegment(m, b, searchtri, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *searchtri;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri crosstri;
+  struct osub crosssubseg;
+  vertex leftvertex, rightvertex;
+  enum finddirectionresult collinear;
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  collinear = finddirection(m, b, searchtri, endpoint2);
+  dest(*searchtri, rightvertex);
+  apex(*searchtri, leftvertex);
+  if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) ||
+      ((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) {
+    /* The segment is already an edge in the mesh. */
+    if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) {
+      lprevself(*searchtri);
+    }
+    /* Insert a subsegment, if there isn't already one there. */
+    insertsubseg(m, b, searchtri, newmark);
+    return 1;
+  } else if (collinear == LEFTCOLLINEAR) {
+    /* We've collided with a vertex between the segment's endpoints. */
+    /* Make the collinear vertex be the triangle's origin. */
+    lprevself(*searchtri);
+    insertsubseg(m, b, searchtri, newmark);
+    /* Insert the remainder of the segment. */
+    return scoutsegment(m, b, searchtri, endpoint2, newmark);
+  } else if (collinear == RIGHTCOLLINEAR) {
+    /* We've collided with a vertex between the segment's endpoints. */
+    insertsubseg(m, b, searchtri, newmark);
+    /* Make the collinear vertex be the triangle's origin. */
+    lnextself(*searchtri);
+    /* Insert the remainder of the segment. */
+    return scoutsegment(m, b, searchtri, endpoint2, newmark);
+  } else {
+    lnext(*searchtri, crosstri);
+    tspivot(crosstri, crosssubseg);
+    /* Check for a crossing segment. */
+    if (crosssubseg.ss == m->dummysub) {
+      return 0;
+    } else {
+      /* Insert a vertex at the intersection. */
+      segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2);
+      otricopy(crosstri, *searchtri);
+      insertsubseg(m, b, searchtri, newmark);
+      /* Insert the remainder of the segment. */
+      return scoutsegment(m, b, searchtri, endpoint2, newmark);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  conformingedge()   Force a segment into a conforming Delaunay            */
+/*                     triangulation by inserting a vertex at its midpoint,  */
+/*                     and recursively forcing in the two half-segments if   */
+/*                     necessary.                                            */
+/*                                                                           */
+/*  Generates a sequence of subsegments connecting `endpoint1' to            */
+/*  `endpoint2'.  `newmark' is the boundary marker of the segment, assigned  */
+/*  to each new splitting vertex and subsegment.                             */
+/*                                                                           */
+/*  Note that conformingedge() does not always maintain the conforming       */
+/*  Delaunay property.  Once inserted, segments are locked into place;       */
+/*  vertices inserted later (to force other segments in) may render these    */
+/*  fixed segments non-Delaunay.  The conforming Delaunay property will be   */
+/*  restored by enforcequality() by splitting encroached subsegments.        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void conformingedge(struct mesh *m, struct behavior *b,
+                    vertex endpoint1, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void conformingedge(m, b, endpoint1, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+vertex endpoint1;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchtri1, searchtri2;
+  struct osub brokensubseg;
+  vertex newvertex;
+  vertex midvertex1, midvertex2;
+  enum insertvertexresult success;
+  int i;
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose > 2) {
+    printf("Forcing segment into triangulation by recursive splitting:\n");
+    printf("  (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1],
+           endpoint2[0], endpoint2[1]);
+  }
+  /* Create a new vertex to insert in the middle of the segment. */
+  newvertex = (vertex) poolalloc(&m->vertices);
+  /* Interpolate coordinates and attributes. */
+  for (i = 0; i < 2 + m->nextras; i++) {
+    newvertex[i] = 0.5 * (endpoint1[i] + endpoint2[i]);
+  }
+  setvertexmark(newvertex, newmark);
+  setvertextype(newvertex, SEGMENTVERTEX);
+  /* No known triangle to search from. */
+  searchtri1.tri = m->dummytri;
+  /* Attempt to insert the new vertex. */
+  success = insertvertex(m, b, newvertex, &searchtri1, (struct osub *) NULL,
+                         0, 0, 0.0);
+  if (success == DUPLICATEVERTEX) {
+    if (b->verbose > 2) {
+      printf("  Segment intersects existing vertex (%.12g, %.12g).\n",
+             newvertex[0], newvertex[1]);
+    }
+    /* Use the vertex that's already there. */
+    vertexdealloc(m, newvertex);
+    org(searchtri1, newvertex);
+  } else {
+    if (success == VIOLATINGVERTEX) {
+      if (b->verbose > 2) {
+        printf("  Two segments intersect at (%.12g, %.12g).\n",
+               newvertex[0], newvertex[1]);
+      }
+      /* By fluke, we've landed right on another segment.  Split it. */
+      tspivot(searchtri1, brokensubseg);
+      success = insertvertex(m, b, newvertex, &searchtri1, &brokensubseg,
+                             0, 0, 0.0);
+      if (success != SUCCESSFULVERTEX) {
+        printf("Internal error in conformingedge():\n");
+        printf("  Failure to split a segment.\n");
+        internalerror();
+      }
+    }
+    /* The vertex has been inserted successfully. */
+    if (m->steinerleft > 0) {
+      m->steinerleft--;
+    }
+  }
+  otricopy(searchtri1, searchtri2);
+  /* `searchtri1' and `searchtri2' are fastened at their origins to         */
+  /*   `newvertex', and will be directed toward `endpoint1' and `endpoint2' */
+  /*   respectively.  First, we must get `searchtri2' out of the way so it  */
+  /*   won't be invalidated during the insertion of the first half of the   */
+  /*   segment.                                                             */
+  finddirection(m, b, &searchtri2, endpoint2);
+  if (!scoutsegment(m, b, &searchtri1, endpoint1, newmark)) {
+    /* The origin of searchtri1 may have changed if a collision with an */
+    /*   intervening vertex on the segment occurred.                    */
+    org(searchtri1, midvertex1);
+    conformingedge(m, b, midvertex1, endpoint1, newmark);
+  }
+  if (!scoutsegment(m, b, &searchtri2, endpoint2, newmark)) {
+    /* The origin of searchtri2 may have changed if a collision with an */
+    /*   intervening vertex on the segment occurred.                    */
+    org(searchtri2, midvertex2);
+    conformingedge(m, b, midvertex2, endpoint2, newmark);
+  }
+}
+
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  delaunayfixup()   Enforce the Delaunay condition at an edge, fanning out */
+/*                    recursively from an existing vertex.  Pay special      */
+/*                    attention to stacking inverted triangles.              */
+/*                                                                           */
+/*  This is a support routine for inserting segments into a constrained      */
+/*  Delaunay triangulation.                                                  */
+/*                                                                           */
+/*  The origin of fixuptri is treated as if it has just been inserted, and   */
+/*  the local Delaunay condition needs to be enforced.  It is only enforced  */
+/*  in one sector, however, that being the angular range defined by          */
+/*  fixuptri.                                                                */
+/*                                                                           */
+/*  This routine also needs to make decisions regarding the "stacking" of    */
+/*  triangles.  (Read the description of constrainededge() below before      */
+/*  reading on here, so you understand the algorithm.)  If the position of   */
+/*  the new vertex (the origin of fixuptri) indicates that the vertex before */
+/*  it on the polygon is a reflex vertex, then "stack" the triangle by       */
+/*  doing nothing.  (fixuptri is an inverted triangle, which is how stacked  */
+/*  triangles are identified.)                                               */
+/*                                                                           */
+/*  Otherwise, check whether the vertex before that was a reflex vertex.     */
+/*  If so, perform an edge flip, thereby eliminating an inverted triangle    */
+/*  (popping it off the stack).  The edge flip may result in the creation    */
+/*  of a new inverted triangle, depending on whether or not the new vertex   */
+/*  is visible to the vertex three edges behind on the polygon.              */
+/*                                                                           */
+/*  If neither of the two vertices behind the new vertex are reflex          */
+/*  vertices, fixuptri and fartri, the triangle opposite it, are not         */
+/*  inverted; hence, ensure that the edge between them is locally Delaunay.  */
+/*                                                                           */
+/*  `leftside' indicates whether or not fixuptri is to the left of the       */
+/*  segment being inserted.  (Imagine that the segment is pointing up from   */
+/*  endpoint1 to endpoint2.)                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void delaunayfixup(struct mesh *m, struct behavior *b,
+                   struct otri *fixuptri, int leftside)
+#else /* not ANSI_DECLARATORS */
+void delaunayfixup(m, b, fixuptri, leftside)
+struct mesh *m;
+struct behavior *b;
+struct otri *fixuptri;
+int leftside;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri neartri;
+  struct otri fartri;
+  struct osub faredge;
+  vertex nearvertex, leftvertex, rightvertex, farvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  lnext(*fixuptri, neartri);
+  sym(neartri, fartri);
+  /* Check if the edge opposite the origin of fixuptri can be flipped. */
+  if (fartri.tri == m->dummytri) {
+    return;
+  }
+  tspivot(neartri, faredge);
+  if (faredge.ss != m->dummysub) {
+    return;
+  }
+  /* Find all the relevant vertices. */
+  apex(neartri, nearvertex);
+  org(neartri, leftvertex);
+  dest(neartri, rightvertex);
+  apex(fartri, farvertex);
+  /* Check whether the previous polygon vertex is a reflex vertex. */
+  if (leftside) {
+    if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) {
+      /* leftvertex is a reflex vertex too.  Nothing can */
+      /*   be done until a convex section is found.      */
+      return;
+    }
+  } else {
+    if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) {
+      /* rightvertex is a reflex vertex too.  Nothing can */
+      /*   be done until a convex section is found.       */
+      return;
+    }
+  }
+  if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) {
+    /* fartri is not an inverted triangle, and farvertex is not a reflex */
+    /*   vertex.  As there are no reflex vertices, fixuptri isn't an     */
+    /*   inverted triangle, either.  Hence, test the edge between the    */
+    /*   triangles to ensure it is locally Delaunay.                     */
+    if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <=
+        0.0) {
+      return;
+    }
+    /* Not locally Delaunay; go on to an edge flip. */
+  }        /* else fartri is inverted; remove it from the stack by flipping. */
+  flip(m, b, &neartri);
+  lprevself(*fixuptri);    /* Restore the origin of fixuptri after the flip. */
+  /* Recursively process the two triangles that result from the flip. */
+  delaunayfixup(m, b, fixuptri, leftside);
+  delaunayfixup(m, b, &fartri, leftside);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  constrainededge()   Force a segment into a constrained Delaunay          */
+/*                      triangulation by deleting the triangles it           */
+/*                      intersects, and triangulating the polygons that      */
+/*                      form on each side of it.                             */
+/*                                                                           */
+/*  Generates a single subsegment connecting `endpoint1' to `endpoint2'.     */
+/*  The triangle `starttri' has `endpoint1' as its origin.  `newmark' is the */
+/*  boundary marker of the segment.                                          */
+/*                                                                           */
+/*  To insert a segment, every triangle whose interior intersects the        */
+/*  segment is deleted.  The union of these deleted triangles is a polygon   */
+/*  (which is not necessarily monotone, but is close enough), which is       */
+/*  divided into two polygons by the new segment.  This routine's task is    */
+/*  to generate the Delaunay triangulation of these two polygons.            */
+/*                                                                           */
+/*  You might think of this routine's behavior as a two-step process.  The   */
+/*  first step is to walk from endpoint1 to endpoint2, flipping each edge    */
+/*  encountered.  This step creates a fan of edges connected to endpoint1,   */
+/*  including the desired edge to endpoint2.  The second step enforces the   */
+/*  Delaunay condition on each side of the segment in an incremental manner: */
+/*  proceeding along the polygon from endpoint1 to endpoint2 (this is done   */
+/*  independently on each side of the segment), each vertex is "enforced"    */
+/*  as if it had just been inserted, but affecting only the previous         */
+/*  vertices.  The result is the same as if the vertices had been inserted   */
+/*  in the order they appear on the polygon, so the result is Delaunay.      */
+/*                                                                           */
+/*  In truth, constrainededge() interleaves these two steps.  The procedure  */
+/*  walks from endpoint1 to endpoint2, and each time an edge is encountered  */
+/*  and flipped, the newly exposed vertex (at the far end of the flipped     */
+/*  edge) is "enforced" upon the previously flipped edges, usually affecting */
+/*  only one side of the polygon (depending upon which side of the segment   */
+/*  the vertex falls on).                                                    */
+/*                                                                           */
+/*  The algorithm is complicated by the need to handle polygons that are not */
+/*  convex.  Although the polygon is not necessarily monotone, it can be     */
+/*  triangulated in a manner similar to the stack-based algorithms for       */
+/*  monotone polygons.  For each reflex vertex (local concavity) of the      */
+/*  polygon, there will be an inverted triangle formed by one of the edge    */
+/*  flips.  (An inverted triangle is one with negative area - that is, its   */
+/*  vertices are arranged in clockwise order - and is best thought of as a   */
+/*  wrinkle in the fabric of the mesh.)  Each inverted triangle can be       */
+/*  thought of as a reflex vertex pushed on the stack, waiting to be fixed   */
+/*  later.                                                                   */
+/*                                                                           */
+/*  A reflex vertex is popped from the stack when a vertex is inserted that  */
+/*  is visible to the reflex vertex.  (However, if the vertex behind the     */
+/*  reflex vertex is not visible to the reflex vertex, a new inverted        */
+/*  triangle will take its place on the stack.)  These details are handled   */
+/*  by the delaunayfixup() routine above.                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void constrainededge(struct mesh *m, struct behavior *b,
+                     struct otri *starttri, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void constrainededge(m, b, starttri, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *starttri;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri fixuptri, fixuptri2;
+  struct osub crosssubseg;
+  vertex endpoint1;
+  vertex farvertex;
+  REAL area;
+  int collision;
+  int done;
+  triangle ptr;             /* Temporary variable used by sym() and oprev(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*starttri, endpoint1);
+  lnext(*starttri, fixuptri);
+  flip(m, b, &fixuptri);
+  /* `collision' indicates whether we have found a vertex directly */
+  /*   between endpoint1 and endpoint2.                            */
+  collision = 0;
+  done = 0;
+  do {
+    org(fixuptri, farvertex);
+    /* `farvertex' is the extreme point of the polygon we are "digging" */
+    /*   to get from endpoint1 to endpoint2.                           */
+    if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) {
+      oprev(fixuptri, fixuptri2);
+      /* Enforce the Delaunay condition around endpoint2. */
+      delaunayfixup(m, b, &fixuptri, 0);
+      delaunayfixup(m, b, &fixuptri2, 1);
+      done = 1;
+    } else {
+      /* Check whether farvertex is to the left or right of the segment */
+      /*   being inserted, to decide which edge of fixuptri to dig      */
+      /*   through next.                                                */
+      area = counterclockwise(m, b, endpoint1, endpoint2, farvertex);
+      if (area == 0.0) {
+        /* We've collided with a vertex between endpoint1 and endpoint2. */
+        collision = 1;
+        oprev(fixuptri, fixuptri2);
+        /* Enforce the Delaunay condition around farvertex. */
+        delaunayfixup(m, b, &fixuptri, 0);
+        delaunayfixup(m, b, &fixuptri2, 1);
+        done = 1;
+      } else {
+        if (area > 0.0) {        /* farvertex is to the left of the segment. */
+          oprev(fixuptri, fixuptri2);
+          /* Enforce the Delaunay condition around farvertex, on the */
+          /*   left side of the segment only.                        */
+          delaunayfixup(m, b, &fixuptri2, 1);
+          /* Flip the edge that crosses the segment.  After the edge is */
+          /*   flipped, one of its endpoints is the fan vertex, and the */
+          /*   destination of fixuptri is the fan vertex.               */
+          lprevself(fixuptri);
+        } else {                /* farvertex is to the right of the segment. */
+          delaunayfixup(m, b, &fixuptri, 0);
+          /* Flip the edge that crosses the segment.  After the edge is */
+          /*   flipped, one of its endpoints is the fan vertex, and the */
+          /*   destination of fixuptri is the fan vertex.               */
+          oprevself(fixuptri);
+        }
+        /* Check for two intersecting segments. */
+        tspivot(fixuptri, crosssubseg);
+        if (crosssubseg.ss == m->dummysub) {
+          flip(m, b, &fixuptri);    /* May create inverted triangle at left. */
+        } else {
+          /* We've collided with a segment between endpoint1 and endpoint2. */
+          collision = 1;
+          /* Insert a vertex at the intersection. */
+          segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2);
+          done = 1;
+        }
+      }
+    }
+  } while (!done);
+  /* Insert a subsegment to make the segment permanent. */
+  insertsubseg(m, b, &fixuptri, newmark);
+  /* If there was a collision with an interceding vertex, install another */
+  /*   segment connecting that vertex with endpoint2.                     */
+  if (collision) {
+    /* Insert the remainder of the segment. */
+    if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) {
+      constrainededge(m, b, &fixuptri, endpoint2, newmark);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  insertsegment()   Insert a PSLG segment into a triangulation.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void insertsegment(struct mesh *m, struct behavior *b,
+                   vertex endpoint1, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void insertsegment(m, b, endpoint1, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+vertex endpoint1;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchtri1, searchtri2;
+  triangle encodedtri;
+  vertex checkvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose > 1) {
+    printf("  Connecting (%.12g, %.12g) to (%.12g, %.12g).\n",
+           endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]);
+  }
+
+  /* Find a triangle whose origin is the segment's first endpoint. */
+  checkvertex = (vertex) NULL;
+  encodedtri = vertex2tri(endpoint1);
+  if (encodedtri != (triangle) NULL) {
+    decode(encodedtri, searchtri1);
+    org(searchtri1, checkvertex);
+  }
+  if (checkvertex != endpoint1) {
+    /* Find a boundary triangle to search from. */
+    searchtri1.tri = m->dummytri;
+    searchtri1.orient = 0;
+    symself(searchtri1);
+    /* Search for the segment's first endpoint by point location. */
+    if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) {
+      printf(
+        "Internal error in insertsegment():  Unable to locate PSLG vertex\n");
+      printf("  (%.12g, %.12g) in triangulation.\n",
+             endpoint1[0], endpoint1[1]);
+      internalerror();
+    }
+  }
+  /* Remember this triangle to improve subsequent point location. */
+  otricopy(searchtri1, m->recenttri);
+  /* Scout the beginnings of a path from the first endpoint */
+  /*   toward the second.                                   */
+  if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) {
+    /* The segment was easily inserted. */
+    return;
+  }
+  /* The first endpoint may have changed if a collision with an intervening */
+  /*   vertex on the segment occurred.                                      */
+  org(searchtri1, endpoint1);
+
+  /* Find a triangle whose origin is the segment's second endpoint. */
+  checkvertex = (vertex) NULL;
+  encodedtri = vertex2tri(endpoint2);
+  if (encodedtri != (triangle) NULL) {
+    decode(encodedtri, searchtri2);
+    org(searchtri2, checkvertex);
+  }
+  if (checkvertex != endpoint2) {
+    /* Find a boundary triangle to search from. */
+    searchtri2.tri = m->dummytri;
+    searchtri2.orient = 0;
+    symself(searchtri2);
+    /* Search for the segment's second endpoint by point location. */
+    if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) {
+      printf(
+        "Internal error in insertsegment():  Unable to locate PSLG vertex\n");
+      printf("  (%.12g, %.12g) in triangulation.\n",
+             endpoint2[0], endpoint2[1]);
+      internalerror();
+    }
+  }
+  /* Remember this triangle to improve subsequent point location. */
+  otricopy(searchtri2, m->recenttri);
+  /* Scout the beginnings of a path from the second endpoint */
+  /*   toward the first.                                     */
+  if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) {
+    /* The segment was easily inserted. */
+    return;
+  }
+  /* The second endpoint may have changed if a collision with an intervening */
+  /*   vertex on the segment occurred.                                       */
+  org(searchtri2, endpoint2);
+
+#ifndef REDUCED
+#ifndef CDT_ONLY
+  if (b->splitseg) {
+    /* Insert vertices to force the segment into the triangulation. */
+    conformingedge(m, b, endpoint1, endpoint2, newmark);
+  } else {
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+    /* Insert the segment directly into the triangulation. */
+    constrainededge(m, b, &searchtri1, endpoint2, newmark);
+#ifndef REDUCED
+#ifndef CDT_ONLY
+  }
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  markhull()   Cover the convex hull of a triangulation with subsegments.  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void markhull(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void markhull(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri hulltri;
+  struct otri nexttri;
+  struct otri starttri;
+  triangle ptr;             /* Temporary variable used by sym() and oprev(). */
+
+  /* Find a triangle handle on the hull. */
+  hulltri.tri = m->dummytri;
+  hulltri.orient = 0;
+  symself(hulltri);
+  /* Remember where we started so we know when to stop. */
+  otricopy(hulltri, starttri);
+  /* Go once counterclockwise around the convex hull. */
+  do {
+    /* Create a subsegment if there isn't already one here. */
+    insertsubseg(m, b, &hulltri, 1);
+    /* To find the next hull edge, go clockwise around the next vertex. */
+    lnextself(hulltri);
+    oprev(hulltri, nexttri);
+    while (nexttri.tri != m->dummytri) {
+      otricopy(nexttri, hulltri);
+      oprev(hulltri, nexttri);
+    }
+  } while (!otriequal(hulltri, starttri));
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  formskeleton()   Create the segments of a triangulation, including PSLG  */
+/*                   segments and edges on the convex hull.                  */
+/*                                                                           */
+/*  The PSLG segments are read from a .poly file.  The return value is the   */
+/*  number of segments in the file.                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist,
+                  int *segmentmarkerlist, int numberofsegments)
+#else /* not ANSI_DECLARATORS */
+void formskeleton(m, b, segmentlist, segmentmarkerlist, numberofsegments)
+struct mesh *m;
+struct behavior *b;
+int *segmentlist;
+int *segmentmarkerlist;
+int numberofsegments;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void formskeleton(struct mesh *m, struct behavior *b,
+                  FILE *polyfile, char *polyfilename)
+#else /* not ANSI_DECLARATORS */
+void formskeleton(m, b, polyfile, polyfilename)
+struct mesh *m;
+struct behavior *b;
+FILE *polyfile;
+char *polyfilename;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  char polyfilename[6];
+  int index;
+#else /* not TRILIBRARY */
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+#endif /* not TRILIBRARY */
+  vertex endpoint1, endpoint2;
+  int segmentmarkers;
+  int end1, end2;
+  int boundmarker;
+  int i;
+
+  if (b->poly) {
+    if (!b->quiet) {
+      printf("Recovering segments in Delaunay triangulation.\n");
+    }
+#ifdef TRILIBRARY
+    strcpy(polyfilename, "input");
+    m->insegments = numberofsegments;
+    segmentmarkers = segmentmarkerlist != (int *) NULL;
+    index = 0;
+#else /* not TRILIBRARY */
+    /* Read the segments from a .poly file. */
+    /* Read number of segments and number of boundary markers. */
+    stringptr = readline(inputline, polyfile, polyfilename);
+    m->insegments = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      segmentmarkers = 0;
+    } else {
+      segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+#endif /* not TRILIBRARY */
+    /* If the input vertices are collinear, there is no triangulation, */
+    /*   so don't try to insert segments.                              */
+    if (m->triangles.items == 0) {
+      return;
+    }
+
+    /* If segments are to be inserted, compute a mapping */
+    /*   from vertices to triangles.                     */
+    if (m->insegments > 0) {
+      makevertexmap(m, b);
+      if (b->verbose) {
+        printf("  Recovering PSLG segments.\n");
+      }
+    }
+
+    boundmarker = 0;
+    /* Read and insert the segments. */
+    for (i = 0; i < m->insegments; i++) {
+#ifdef TRILIBRARY
+      end1 = segmentlist[index++];
+      end2 = segmentlist[index++];
+      if (segmentmarkers) {
+        boundmarker = segmentmarkerlist[i];
+      }
+#else /* not TRILIBRARY */
+      stringptr = readline(inputline, polyfile, b->inpolyfilename);
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %d has no endpoints in %s.\n",
+               b->firstnumber + i, polyfilename);
+        exit(1);
+      } else {
+        end1 = (int) strtol(stringptr, &stringptr, 0);
+      }
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %d is missing its second endpoint in %s.\n",
+               b->firstnumber + i, polyfilename);
+        exit(1);
+      } else {
+        end2 = (int) strtol(stringptr, &stringptr, 0);
+      }
+      if (segmentmarkers) {
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          boundmarker = 0;
+        } else {
+          boundmarker = (int) strtol(stringptr, &stringptr, 0);
+        }
+      }
+#endif /* not TRILIBRARY */
+      if ((end1 < b->firstnumber) ||
+          (end1 >= b->firstnumber + m->invertices)) {
+        if (!b->quiet) {
+          printf("Warning:  Invalid first endpoint of segment %d in %s.\n",
+                 b->firstnumber + i, polyfilename);
+        }
+      } else if ((end2 < b->firstnumber) ||
+                 (end2 >= b->firstnumber + m->invertices)) {
+        if (!b->quiet) {
+          printf("Warning:  Invalid second endpoint of segment %d in %s.\n",
+                 b->firstnumber + i, polyfilename);
+        }
+      } else {
+        endpoint1 = getvertex(m, b, end1);
+        endpoint2 = getvertex(m, b, end2);
+        if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) {
+          if (!b->quiet) {
+            printf("Warning:  Endpoints of segment %d are coincident in %s.\n",
+                   b->firstnumber + i, polyfilename);
+          }
+        } else {
+          insertsegment(m, b, endpoint1, endpoint2, boundmarker);
+        }
+      }
+    }
+  } else {
+    m->insegments = 0;
+  }
+  if (b->convex || !b->poly) {
+    /* Enclose the convex hull with subsegments. */
+    if (b->verbose) {
+      printf("  Enclosing convex hull with segments.\n");
+    }
+    markhull(m, b);
+  }
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Segment insertion ends here                               *********/
+
+/********* Carving out holes and concavities begins here             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  infecthull()   Virally infect all of the triangles of the convex hull    */
+/*                 that are not protected by subsegments.  Where there are   */
+/*                 subsegments, set boundary markers as appropriate.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void infecthull(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void infecthull(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri hulltri;
+  struct otri nexttri;
+  struct otri starttri;
+  struct osub hullsubseg;
+  triangle **deadtriangle;
+  vertex horg, hdest;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose) {
+    printf("  Marking concavities (external triangles) for elimination.\n");
+  }
+  /* Find a triangle handle on the hull. */
+  hulltri.tri = m->dummytri;
+  hulltri.orient = 0;
+  symself(hulltri);
+  /* Remember where we started so we know when to stop. */
+  otricopy(hulltri, starttri);
+  /* Go once counterclockwise around the convex hull. */
+  do {
+    /* Ignore triangles that are already infected. */
+    if (!infected(hulltri)) {
+      /* Is the triangle protected by a subsegment? */
+      tspivot(hulltri, hullsubseg);
+      if (hullsubseg.ss == m->dummysub) {
+        /* The triangle is not protected; infect it. */
+        if (!infected(hulltri)) {
+          infect(hulltri);
+          deadtriangle = (triangle **) poolalloc(&m->viri);
+          *deadtriangle = hulltri.tri;
+        }
+      } else {
+        /* The triangle is protected; set boundary markers if appropriate. */
+        if (mark(hullsubseg) == 0) {
+          setmark(hullsubseg, 1);
+          org(hulltri, horg);
+          dest(hulltri, hdest);
+          if (vertexmark(horg) == 0) {
+            setvertexmark(horg, 1);
+          }
+          if (vertexmark(hdest) == 0) {
+            setvertexmark(hdest, 1);
+          }
+        }
+      }
+    }
+    /* To find the next hull edge, go clockwise around the next vertex. */
+    lnextself(hulltri);
+    oprev(hulltri, nexttri);
+    while (nexttri.tri != m->dummytri) {
+      otricopy(nexttri, hulltri);
+      oprev(hulltri, nexttri);
+    }
+  } while (!otriequal(hulltri, starttri));
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  plague()   Spread the virus from all infected triangles to any neighbors */
+/*             not protected by subsegments.  Delete all infected triangles. */
+/*                                                                           */
+/*  This is the procedure that actually creates holes and concavities.       */
+/*                                                                           */
+/*  This procedure operates in two phases.  The first phase identifies all   */
+/*  the triangles that will die, and marks them as infected.  They are       */
+/*  marked to ensure that each triangle is added to the virus pool only      */
+/*  once, so the procedure will terminate.                                   */
+/*                                                                           */
+/*  The second phase actually eliminates the infected triangles.  It also    */
+/*  eliminates orphaned vertices.                                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void plague(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void plague(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri testtri;
+  struct otri neighbor;
+  triangle **virusloop;
+  triangle **deadtriangle;
+  struct osub neighborsubseg;
+  vertex testvertex;
+  vertex norg, ndest;
+  vertex deadorg, deaddest, deadapex;
+  int killorg;
+  triangle ptr;             /* Temporary variable used by sym() and onext(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose) {
+    printf("  Marking neighbors of marked triangles.\n");
+  }
+  /* Loop through all the infected triangles, spreading the virus to */
+  /*   their neighbors, then to their neighbors' neighbors.          */
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+    /* A triangle is marked as infected by messing with one of its pointers */
+    /*   to subsegments, setting it to an illegal value.  Hence, we have to */
+    /*   temporarily uninfect this triangle so that we can examine its      */
+    /*   adjacent subsegments.                                              */
+    uninfect(testtri);
+    if (b->verbose > 2) {
+      /* Assign the triangle an orientation for convenience in */
+      /*   checking its vertices.                              */
+      testtri.orient = 0;
+      org(testtri, deadorg);
+      dest(testtri, deaddest);
+      apex(testtri, deadapex);
+      printf("    Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+             deadorg[0], deadorg[1], deaddest[0], deaddest[1],
+             deadapex[0], deadapex[1]);
+    }
+    /* Check each of the triangle's three neighbors. */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      /* Find the neighbor. */
+      sym(testtri, neighbor);
+      /* Check for a subsegment between the triangle and its neighbor. */
+      tspivot(testtri, neighborsubseg);
+      /* Check if the neighbor is nonexistent or already infected. */
+      if ((neighbor.tri == m->dummytri) || infected(neighbor)) {
+        if (neighborsubseg.ss != m->dummysub) {
+          /* There is a subsegment separating the triangle from its      */
+          /*   neighbor, but both triangles are dying, so the subsegment */
+          /*   dies too.                                                 */
+          subsegdealloc(m, neighborsubseg.ss);
+          if (neighbor.tri != m->dummytri) {
+            /* Make sure the subsegment doesn't get deallocated again */
+            /*   later when the infected neighbor is visited.         */
+            uninfect(neighbor);
+            tsdissolve(neighbor);
+            infect(neighbor);
+          }
+        }
+      } else {                   /* The neighbor exists and is not infected. */
+        if (neighborsubseg.ss == m->dummysub) {
+          /* There is no subsegment protecting the neighbor, so */
+          /*   the neighbor becomes infected.                   */
+          if (b->verbose > 2) {
+            org(neighbor, deadorg);
+            dest(neighbor, deaddest);
+            apex(neighbor, deadapex);
+            printf(
+              "    Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+                   deadorg[0], deadorg[1], deaddest[0], deaddest[1],
+                   deadapex[0], deadapex[1]);
+          }
+          infect(neighbor);
+          /* Ensure that the neighbor's neighbors will be infected. */
+          deadtriangle = (triangle **) poolalloc(&m->viri);
+          *deadtriangle = neighbor.tri;
+        } else {               /* The neighbor is protected by a subsegment. */
+          /* Remove this triangle from the subsegment. */
+          stdissolve(neighborsubseg);
+          /* The subsegment becomes a boundary.  Set markers accordingly. */
+          if (mark(neighborsubseg) == 0) {
+            setmark(neighborsubseg, 1);
+          }
+          org(neighbor, norg);
+          dest(neighbor, ndest);
+          if (vertexmark(norg) == 0) {
+            setvertexmark(norg, 1);
+          }
+          if (vertexmark(ndest) == 0) {
+            setvertexmark(ndest, 1);
+          }
+        }
+      }
+    }
+    /* Remark the triangle as infected, so it doesn't get added to the */
+    /*   virus pool again.                                             */
+    infect(testtri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+
+  if (b->verbose) {
+    printf("  Deleting marked triangles.\n");
+  }
+
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+
+    /* Check each of the three corners of the triangle for elimination. */
+    /*   This is done by walking around each vertex, checking if it is  */
+    /*   still connected to at least one live triangle.                 */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      org(testtri, testvertex);
+      /* Check if the vertex has already been tested. */
+      if (testvertex != (vertex) NULL) {
+        killorg = 1;
+        /* Mark the corner of the triangle as having been tested. */
+        setorg(testtri, NULL);
+        /* Walk counterclockwise about the vertex. */
+        onext(testtri, neighbor);
+        /* Stop upon reaching a boundary or the starting triangle. */
+        while ((neighbor.tri != m->dummytri) &&
+               (!otriequal(neighbor, testtri))) {
+          if (infected(neighbor)) {
+            /* Mark the corner of this triangle as having been tested. */
+            setorg(neighbor, NULL);
+          } else {
+            /* A live triangle.  The vertex survives. */
+            killorg = 0;
+          }
+          /* Walk counterclockwise about the vertex. */
+          onextself(neighbor);
+        }
+        /* If we reached a boundary, we must walk clockwise as well. */
+        if (neighbor.tri == m->dummytri) {
+          /* Walk clockwise about the vertex. */
+          oprev(testtri, neighbor);
+          /* Stop upon reaching a boundary. */
+          while (neighbor.tri != m->dummytri) {
+            if (infected(neighbor)) {
+            /* Mark the corner of this triangle as having been tested. */
+              setorg(neighbor, NULL);
+            } else {
+              /* A live triangle.  The vertex survives. */
+              killorg = 0;
+            }
+            /* Walk clockwise about the vertex. */
+            oprevself(neighbor);
+          }
+        }
+        if (killorg) {
+          if (b->verbose > 1) {
+            printf("    Deleting vertex (%.12g, %.12g)\n",
+                   testvertex[0], testvertex[1]);
+          }
+          setvertextype(testvertex, UNDEADVERTEX);
+          m->undeads++;
+        }
+      }
+    }
+
+    /* Record changes in the number of boundary edges, and disconnect */
+    /*   dead triangles from their neighbors.                         */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      sym(testtri, neighbor);
+      if (neighbor.tri == m->dummytri) {
+        /* There is no neighboring triangle on this edge, so this edge    */
+        /*   is a boundary edge.  This triangle is being deleted, so this */
+        /*   boundary edge is deleted.                                    */
+        m->hullsize--;
+      } else {
+        /* Disconnect the triangle from its neighbor. */
+        dissolve(neighbor);
+        /* There is a neighboring triangle on this edge, so this edge */
+        /*   becomes a boundary edge when this triangle is deleted.   */
+        m->hullsize++;
+      }
+    }
+    /* Return the dead triangle to the pool of triangles. */
+    triangledealloc(m, testtri.tri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+  /* Empty the virus pool. */
+  poolrestart(&m->viri);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  regionplague()   Spread regional attributes and/or area constraints      */
+/*                   (from a .poly file) throughout the mesh.                */
+/*                                                                           */
+/*  This procedure operates in two phases.  The first phase spreads an       */
+/*  attribute and/or an area constraint through a (segment-bounded) region.  */
+/*  The triangles are marked to ensure that each triangle is added to the    */
+/*  virus pool only once, so the procedure will terminate.                   */
+/*                                                                           */
+/*  The second phase uninfects all infected triangles, returning them to     */
+/*  normal.                                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void regionplague(struct mesh *m, struct behavior *b,
+                  REAL attribute, REAL area)
+#else /* not ANSI_DECLARATORS */
+void regionplague(m, b, attribute, area)
+struct mesh *m;
+struct behavior *b;
+REAL attribute;
+REAL area;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri testtri;
+  struct otri neighbor;
+  triangle **virusloop;
+  triangle **regiontri;
+  struct osub neighborsubseg;
+  vertex regionorg, regiondest, regionapex;
+  triangle ptr;             /* Temporary variable used by sym() and onext(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose > 1) {
+    printf("  Marking neighbors of marked triangles.\n");
+  }
+  /* Loop through all the infected triangles, spreading the attribute      */
+  /*   and/or area constraint to their neighbors, then to their neighbors' */
+  /*   neighbors.                                                          */
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+    /* A triangle is marked as infected by messing with one of its pointers */
+    /*   to subsegments, setting it to an illegal value.  Hence, we have to */
+    /*   temporarily uninfect this triangle so that we can examine its      */
+    /*   adjacent subsegments.                                              */
+    uninfect(testtri);
+    if (b->regionattrib) {
+      /* Set an attribute. */
+      setelemattribute(testtri, m->eextras, attribute);
+    }
+    if (b->vararea) {
+      /* Set an area constraint. */
+      setareabound(testtri, area);
+    }
+    if (b->verbose > 2) {
+      /* Assign the triangle an orientation for convenience in */
+      /*   checking its vertices.                              */
+      testtri.orient = 0;
+      org(testtri, regionorg);
+      dest(testtri, regiondest);
+      apex(testtri, regionapex);
+      printf("    Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+             regionorg[0], regionorg[1], regiondest[0], regiondest[1],
+             regionapex[0], regionapex[1]);
+    }
+    /* Check each of the triangle's three neighbors. */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      /* Find the neighbor. */
+      sym(testtri, neighbor);
+      /* Check for a subsegment between the triangle and its neighbor. */
+      tspivot(testtri, neighborsubseg);
+      /* Make sure the neighbor exists, is not already infected, and */
+      /*   isn't protected by a subsegment.                          */
+      if ((neighbor.tri != m->dummytri) && !infected(neighbor)
+          && (neighborsubseg.ss == m->dummysub)) {
+        if (b->verbose > 2) {
+          org(neighbor, regionorg);
+          dest(neighbor, regiondest);
+          apex(neighbor, regionapex);
+          printf("    Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+                 regionorg[0], regionorg[1], regiondest[0], regiondest[1],
+                 regionapex[0], regionapex[1]);
+        }
+        /* Infect the neighbor. */
+        infect(neighbor);
+        /* Ensure that the neighbor's neighbors will be infected. */
+        regiontri = (triangle **) poolalloc(&m->viri);
+        *regiontri = neighbor.tri;
+      }
+    }
+    /* Remark the triangle as infected, so it doesn't get added to the */
+    /*   virus pool again.                                             */
+    infect(testtri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+
+  /* Uninfect all triangles. */
+  if (b->verbose > 1) {
+    printf("  Unmarking marked triangles.\n");
+  }
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+    uninfect(testtri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+  /* Empty the virus pool. */
+  poolrestart(&m->viri);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  carveholes()   Find the holes and infect them.  Find the area            */
+/*                 constraints and infect them.  Infect the convex hull.     */
+/*                 Spread the infection and kill triangles.  Spread the      */
+/*                 area constraints.                                         */
+/*                                                                           */
+/*  This routine mainly calls other routines to carry out all these          */
+/*  functions.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes,
+                REAL *regionlist, int regions)
+#else /* not ANSI_DECLARATORS */
+void carveholes(m, b, holelist, holes, regionlist, regions)
+struct mesh *m;
+struct behavior *b;
+REAL *holelist;
+int holes;
+REAL *regionlist;
+int regions;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchtri;
+  struct otri triangleloop;
+  struct otri *regiontris;
+  triangle **holetri;
+  triangle **regiontri;
+  vertex searchorg, searchdest;
+  enum locateresult intersect;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (!(b->quiet || (b->noholes && b->convex))) {
+    printf("Removing unwanted triangles.\n");
+    if (b->verbose && (holes > 0)) {
+      printf("  Marking holes for elimination.\n");
+    }
+  }
+
+  if (regions > 0) {
+    /* Allocate storage for the triangles in which region points fall. */
+    regiontris = (struct otri *)
+                 trimalloc(regions * (int) sizeof(struct otri));
+  }
+
+  if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
+    /* Initialize a pool of viri to be used for holes, concavities, */
+    /*   regional attributes, and/or regional area constraints.     */
+    poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK,
+             POINTER, 0);
+  }
+
+  if (!b->convex) {
+    /* Mark as infected any unprotected triangles on the boundary. */
+    /*   This is one way by which concavities are created.         */
+    infecthull(m, b);
+  }
+
+  if ((holes > 0) && !b->noholes) {
+    /* Infect each triangle in which a hole lies. */
+    for (i = 0; i < 2 * holes; i += 2) {
+      /* Ignore holes that aren't within the bounds of the mesh. */
+      if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax)
+          && (holelist[i + 1] >= m->ymin) && (holelist[i + 1] <= m->ymax)) {
+        /* Start searching from some triangle on the outer boundary. */
+        searchtri.tri = m->dummytri;
+        searchtri.orient = 0;
+        symself(searchtri);
+        /* Ensure that the hole is to the left of this boundary edge; */
+        /*   otherwise, locate() will falsely report that the hole    */
+        /*   falls within the starting triangle.                      */
+        org(searchtri, searchorg);
+        dest(searchtri, searchdest);
+        if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) >
+            0.0) {
+          /* Find a triangle that contains the hole. */
+          intersect = locate(m, b, &holelist[i], &searchtri);
+          if ((intersect != OUTSIDE) && (!infected(searchtri))) {
+            /* Infect the triangle.  This is done by marking the triangle  */
+            /*   as infected and including the triangle in the virus pool. */
+            infect(searchtri);
+            holetri = (triangle **) poolalloc(&m->viri);
+            *holetri = searchtri.tri;
+          }
+        }
+      }
+    }
+  }
+
+  /* Now, we have to find all the regions BEFORE we carve the holes, because */
+  /*   locate() won't work when the triangulation is no longer convex.       */
+  /*   (Incidentally, this is the reason why regional attributes and area    */
+  /*   constraints can't be used when refining a preexisting mesh, which     */
+  /*   might not be convex; they can only be used with a freshly             */
+  /*   triangulated PSLG.)                                                   */
+  if (regions > 0) {
+    /* Find the starting triangle for each region. */
+    for (i = 0; i < regions; i++) {
+      regiontris[i].tri = m->dummytri;
+      /* Ignore region points that aren't within the bounds of the mesh. */
+      if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) &&
+          (regionlist[4 * i + 1] >= m->ymin) &&
+          (regionlist[4 * i + 1] <= m->ymax)) {
+        /* Start searching from some triangle on the outer boundary. */
+        searchtri.tri = m->dummytri;
+        searchtri.orient = 0;
+        symself(searchtri);
+        /* Ensure that the region point is to the left of this boundary */
+        /*   edge; otherwise, locate() will falsely report that the     */
+        /*   region point falls within the starting triangle.           */
+        org(searchtri, searchorg);
+        dest(searchtri, searchdest);
+        if (counterclockwise(m, b, searchorg, searchdest, &regionlist[4 * i]) >
+            0.0) {
+          /* Find a triangle that contains the region point. */
+          intersect = locate(m, b, &regionlist[4 * i], &searchtri);
+          if ((intersect != OUTSIDE) && (!infected(searchtri))) {
+            /* Record the triangle for processing after the */
+            /*   holes have been carved.                    */
+            otricopy(searchtri, regiontris[i]);
+          }
+        }
+      }
+    }
+  }
+
+  if (m->viri.items > 0) {
+    /* Carve the holes and concavities. */
+    plague(m, b);
+  }
+  /* The virus pool should be empty now. */
+
+  if (regions > 0) {
+    if (!b->quiet) {
+      if (b->regionattrib) {
+        if (b->vararea) {
+          printf("Spreading regional attributes and area constraints.\n");
+        } else {
+          printf("Spreading regional attributes.\n");
+        }
+      } else { 
+        printf("Spreading regional area constraints.\n");
+      }
+    }
+    if (b->regionattrib && !b->refine) {
+      /* Assign every triangle a regional attribute of zero. */
+      traversalinit(&m->triangles);
+      triangleloop.orient = 0;
+      triangleloop.tri = triangletraverse(m);
+      while (triangleloop.tri != (triangle *) NULL) {
+        setelemattribute(triangleloop, m->eextras, 0.0);
+        triangleloop.tri = triangletraverse(m);
+      }
+    }
+    for (i = 0; i < regions; i++) {
+      if (regiontris[i].tri != m->dummytri) {
+        /* Make sure the triangle under consideration still exists. */
+        /*   It may have been eaten by the virus.                   */
+        if (!deadtri(regiontris[i].tri)) {
+          /* Put one triangle in the virus pool. */
+          infect(regiontris[i]);
+          regiontri = (triangle **) poolalloc(&m->viri);
+          *regiontri = regiontris[i].tri;
+          /* Apply one region's attribute and/or area constraint. */
+          regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]);
+          /* The virus pool should be empty now. */
+        }
+      }
+    }
+    if (b->regionattrib && !b->refine) {
+      /* Note the fact that each triangle has an additional attribute. */
+      m->eextras++;
+    }
+  }
+
+  /* Free up memory. */
+  if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
+    pooldeinit(&m->viri);
+  }
+  if (regions > 0) {
+    trifree((VOID *) regiontris);
+  }
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Carving out holes and concavities ends here               *********/
+
+/********* Mesh quality maintenance begins here                      *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  tallyencs()   Traverse the entire list of subsegments, and check each    */
+/*                to see if it is encroached.  If so, add it to the list.    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void tallyencs(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void tallyencs(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct osub subsegloop;
+  int dummy;
+
+  traversalinit(&m->subsegs);
+  subsegloop.ssorient = 0;
+  subsegloop.ss = subsegtraverse(m);
+  while (subsegloop.ss != (subseg *) NULL) {
+    /* If the segment is encroached, add it to the list. */
+    dummy = checkseg4encroach(m, b, &subsegloop, 0.0);
+    subsegloop.ss = subsegtraverse(m);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  precisionerror()  Print an error message for precision problems.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+void precisionerror()
+{
+  printf("Try increasing the area criterion and/or reducing the minimum\n");
+  printf("  allowable angle so that tiny triangles are not created.\n");
+#ifdef SINGLE
+  printf("Alternatively, try recompiling me with double precision\n");
+  printf("  arithmetic (by removing \"#define SINGLE\" from the\n");
+  printf("  source file or \"-DSINGLE\" from the makefile).\n");
+#endif /* SINGLE */
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  splitencsegs()   Split all the encroached subsegments.                   */
+/*                                                                           */
+/*  Each encroached subsegment is repaired by splitting it - inserting a     */
+/*  vertex at or near its midpoint.  Newly inserted vertices may encroach    */
+/*  upon other subsegments; these are also repaired.                         */
+/*                                                                           */
+/*  `triflaws' is a flag that specifies whether one should take note of new  */
+/*  bad triangles that result from inserting vertices to repair encroached   */
+/*  subsegments.                                                             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void splitencsegs(struct mesh *m, struct behavior *b, int triflaws)
+#else /* not ANSI_DECLARATORS */
+void splitencsegs(m, b, triflaws)
+struct mesh *m;
+struct behavior *b;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri enctri;
+  struct otri testtri;
+  struct osub testsh;
+  struct osub currentenc;
+  struct badsubseg *encloop;
+  vertex eorg, edest, eapex;
+  vertex newvertex;
+  enum insertvertexresult success;
+  REAL segmentlength, nearestpoweroftwo;
+  REAL split;
+  REAL multiplier, divisor;
+  int acuteorg, acuteorg2, acutedest, acutedest2;
+  int dummy;
+  int i;
+  triangle ptr;                     /* Temporary variable used by stpivot(). */
+  subseg sptr;                        /* Temporary variable used by snext(). */
+
+  /* Note that steinerleft == -1 if an unlimited number */
+  /*   of Steiner points is allowed.                    */
+  while ((m->badsubsegs.items > 0) && (m->steinerleft != 0)) {
+    traversalinit(&m->badsubsegs);
+    encloop = badsubsegtraverse(m);
+    while ((encloop != (struct badsubseg *) NULL) && (m->steinerleft != 0)) {
+      sdecode(encloop->encsubseg, currentenc);
+      sorg(currentenc, eorg);
+      sdest(currentenc, edest);
+      /* Make sure that this segment is still the same segment it was   */
+      /*   when it was determined to be encroached.  If the segment was */
+      /*   enqueued multiple times (because several newly inserted      */
+      /*   vertices encroached it), it may have already been split.     */
+      if (!deadsubseg(currentenc.ss) &&
+          (eorg == encloop->subsegorg) && (edest == encloop->subsegdest)) {
+        /* To decide where to split a segment, we need to know if the   */
+        /*   segment shares an endpoint with an adjacent segment.       */
+        /*   The concern is that, if we simply split every encroached   */
+        /*   segment in its center, two adjacent segments with a small  */
+        /*   angle between them might lead to an infinite loop; each    */
+        /*   vertex added to split one segment will encroach upon the   */
+        /*   other segment, which must then be split with a vertex that */
+        /*   will encroach upon the first segment, and so on forever.   */
+        /* To avoid this, imagine a set of concentric circles, whose    */
+        /*   radii are powers of two, about each segment endpoint.      */
+        /*   These concentric circles determine where the segment is    */
+        /*   split.  (If both endpoints are shared with adjacent        */
+        /*   segments, split the segment in the middle, and apply the   */
+        /*   concentric circles for later splittings.)                  */
+
+        /* Is the origin shared with another segment? */
+        stpivot(currentenc, enctri);
+        lnext(enctri, testtri);
+        tspivot(testtri, testsh);
+        acuteorg = testsh.ss != m->dummysub;
+        /* Is the destination shared with another segment? */
+        lnextself(testtri);
+        tspivot(testtri, testsh);
+        acutedest = testsh.ss != m->dummysub;
+
+        /* If we're using diametral lenses (rather than diametral circles) */
+        /*   to define encroachment, delete free vertices from the         */
+        /*   subsegment's diametral circle.                                */
+        if (!b->nolenses && !acuteorg && !acutedest) {
+          apex(enctri, eapex);
+          while ((vertextype(eapex) == FREEVERTEX) &&
+                 ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                  (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
+            deletevertex(m, b, &testtri);
+            stpivot(currentenc, enctri);
+            apex(enctri, eapex);
+            lprev(enctri, testtri);
+          }
+        }
+
+        /* Now, check the other side of the segment, if there's a triangle */
+        /*   there.                                                        */
+        sym(enctri, testtri);
+        if (testtri.tri != m->dummytri) {
+          /* Is the destination shared with another segment? */
+          lnextself(testtri);
+          tspivot(testtri, testsh);
+          acutedest2 = testsh.ss != m->dummysub;
+          acutedest = acutedest || acutedest2;
+          /* Is the origin shared with another segment? */
+          lnextself(testtri);
+          tspivot(testtri, testsh);
+          acuteorg2 = testsh.ss != m->dummysub;
+          acuteorg = acuteorg || acuteorg2;
+
+          /* Delete free vertices from the subsegment's diametral circle. */
+          if (!b->nolenses && !acuteorg2 && !acutedest2) {
+            org(testtri, eapex);
+            while ((vertextype(eapex) == FREEVERTEX) &&
+                   ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                    (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
+              deletevertex(m, b, &testtri);
+              sym(enctri, testtri);
+              apex(testtri, eapex);
+              lprevself(testtri);
+            }
+          }
+        }
+
+        /* Use the concentric circles if exactly one endpoint is shared */
+        /*   with another adjacent segment.                             */
+        if (acuteorg || acutedest) {
+          segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) +
+                               (edest[1] - eorg[1]) * (edest[1] - eorg[1]));
+          /* Find the power of two that most evenly splits the segment.  */
+          /*   The worst case is a 2:1 ratio between subsegment lengths. */
+          nearestpoweroftwo = 1.0;
+          while (segmentlength > 3.0 * nearestpoweroftwo) {
+            nearestpoweroftwo *= 2.0;
+          }
+          while (segmentlength < 1.5 * nearestpoweroftwo) {
+            nearestpoweroftwo *= 0.5;
+          }
+          /* Where do we split the segment? */
+          split = nearestpoweroftwo / segmentlength;
+          if (acutedest) {
+            split = 1.0 - split;
+          }
+        } else {
+          /* If we're not worried about adjacent segments, split */
+          /*   this segment in the middle.                       */
+          split = 0.5;
+        }
+
+        /* Create the new vertex. */
+        newvertex = (vertex) poolalloc(&m->vertices);
+        /* Interpolate its coordinate and attributes. */
+        for (i = 0; i < 2 + m->nextras; i++) {
+          newvertex[i] = eorg[i] + split * (edest[i] - eorg[i]);
+        }
+
+        if (!b->noexact) {
+          /* Roundoff in the above calculation may yield a `newvertex'   */
+          /*   that is not precisely collinear with `eorg' and `edest'.  */
+          /*   Improve collinearity by one step of iterative refinement. */
+          multiplier = counterclockwise(m, b, eorg, edest, newvertex);
+          divisor = ((eorg[0] - edest[0]) * (eorg[0] - edest[0]) +
+                     (eorg[1] - edest[1]) * (eorg[1] - edest[1]));
+          if ((multiplier != 0.0) && (divisor != 0.0)) {
+            multiplier = multiplier / divisor;
+            /* Watch out for NANs. */
+            if (multiplier == multiplier) {
+              newvertex[0] += multiplier * (edest[1] - eorg[1]);
+              newvertex[1] += multiplier * (eorg[0] - edest[0]);
+            }
+          }
+        }
+
+        setvertexmark(newvertex, mark(currentenc));
+        setvertextype(newvertex, SEGMENTVERTEX);
+        if (b->verbose > 1) {
+          printf(
+  "  Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
+                 eorg[0], eorg[1], edest[0], edest[1],
+                 newvertex[0], newvertex[1]);
+        }
+        /* Check whether the new vertex lies on an endpoint. */
+        if (((newvertex[0] == eorg[0]) && (newvertex[1] == eorg[1])) ||
+            ((newvertex[0] == edest[0]) && (newvertex[1] == edest[1]))) {
+          printf("Error:  Ran out of precision at (%.12g, %.12g).\n",
+                 newvertex[0], newvertex[1]);
+          printf("I attempted to split a segment to a smaller size than\n");
+          printf("  can be accommodated by the finite precision of\n");
+          printf("  floating point arithmetic.\n");
+          precisionerror();
+          exit(1);
+        }
+        /* Insert the splitting vertex.  This should always succeed. */
+        success = insertvertex(m, b, newvertex, &enctri, &currentenc,
+                               1, triflaws, 0.0);
+        if ((success != SUCCESSFULVERTEX) && (success != ENCROACHINGVERTEX)) {
+          printf("Internal error in splitencsegs():\n");
+          printf("  Failure to split a segment.\n");
+          internalerror();
+        }
+        if (m->steinerleft > 0) {
+          m->steinerleft--;
+        }
+        /* Check the two new subsegments to see if they're encroached. */
+        dummy = checkseg4encroach(m, b, &currentenc, 0.0);
+        snextself(currentenc);
+        dummy = checkseg4encroach(m, b, &currentenc, 0.0);
+      }
+
+      badsubsegdealloc(m, encloop);
+      encloop = badsubsegtraverse(m);
+    }
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  tallyfaces()   Test every triangle in the mesh for quality measures.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void tallyfaces(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void tallyfaces(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+
+  if (b->verbose) {
+    printf("  Making a list of bad triangles.\n");
+  }
+  traversalinit(&m->triangles);
+  triangleloop.orient = 0;
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* If the triangle is bad, enqueue it. */
+    testtriangle(m, b, &triangleloop);
+    triangleloop.tri = triangletraverse(m);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  splittriangle()   Inserts a vertex at the circumcenter of a triangle.    */
+/*                    Deletes the newly inserted vertex if it encroaches     */
+/*                    upon a segment.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void splittriangle(struct mesh *m, struct behavior *b,
+                   struct badtriang *badtri)
+#else /* not ANSI_DECLARATORS */
+void splittriangle(m, b, badtri)
+struct mesh *m;
+struct behavior *b;
+struct badtriang *badtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri badotri;
+  vertex borg, bdest, bapex;
+  vertex newvertex;
+  REAL xi, eta;
+  REAL minedge;
+  enum insertvertexresult success;
+  int errorflag;
+  int i;
+
+  decode(badtri->poortri, badotri);
+  org(badotri, borg);
+  dest(badotri, bdest);
+  apex(badotri, bapex);
+  /* Make sure that this triangle is still the same triangle it was      */
+  /*   when it was tested and determined to be of bad quality.           */
+  /*   Subsequent transformations may have made it a different triangle. */
+  if (!deadtri(badotri.tri) && (borg == badtri->triangorg) &&
+      (bdest == badtri->triangdest) && (bapex == badtri->triangapex)) {
+    if (b->verbose > 1) {
+      printf("  Splitting this triangle at its circumcenter:\n");
+      printf("    (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", borg[0],
+             borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
+    }
+
+    errorflag = 0;
+    /* Create a new vertex at the triangle's circumcenter. */
+    newvertex = (vertex) poolalloc(&m->vertices);
+    findcircumcenter(m, b, borg, bdest, bapex, newvertex, &xi, &eta, &minedge,
+                     1);
+
+    /* Check whether the new vertex lies on a triangle vertex. */
+    if (((newvertex[0] == borg[0]) && (newvertex[1] == borg[1])) ||
+        ((newvertex[0] == bdest[0]) && (newvertex[1] == bdest[1])) ||
+        ((newvertex[0] == bapex[0]) && (newvertex[1] == bapex[1]))) {
+      if (!b->quiet) {
+        printf(
+            "Warning:  New vertex (%.12g, %.12g) falls on existing vertex.\n",
+               newvertex[0], newvertex[1]);
+        errorflag = 1;
+      }
+      vertexdealloc(m, newvertex);
+    } else {
+      for (i = 2; i < 2 + m->nextras; i++) {
+        /* Interpolate the vertex attributes at the circumcenter. */
+        newvertex[i] = borg[i] + xi * (bdest[i] - borg[i])
+                              + eta * (bapex[i] - borg[i]);
+      }
+      /* The new vertex must be in the interior, and therefore is a */
+      /*   free vertex with a marker of zero.                       */
+      setvertexmark(newvertex, 0);
+      setvertextype(newvertex, FREEVERTEX);
+
+      /* Ensure that the handle `badotri' does not represent the longest  */
+      /*   edge of the triangle.  This ensures that the circumcenter must */
+      /*   fall to the left of this edge, so point location will work.    */
+      /*   (If the angle org-apex-dest exceeds 90 degrees, then the       */
+      /*   circumcenter lies outside the org-dest edge, and eta is        */
+      /*   negative.  Roundoff error might prevent eta from being         */
+      /*   negative when it should be, so I test eta against xi.)         */
+      if (eta < xi) {
+        lprevself(badotri);
+      }
+
+      /* Insert the circumcenter, searching from the edge of the triangle, */
+      /*   and maintain the Delaunay property of the triangulation.        */
+      success = insertvertex(m, b, newvertex, &badotri, (struct osub *) NULL,
+                             1, 1, minedge);
+      if (success == SUCCESSFULVERTEX) {
+        if (m->steinerleft > 0) {
+          m->steinerleft--;
+        }
+      } else if (success == ENCROACHINGVERTEX) {
+        /* If the newly inserted vertex encroaches upon a subsegment, */
+        /*   delete the new vertex.                                   */
+        undovertex(m, b);
+        if (b->verbose > 1) {
+          printf("  Rejecting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+        }
+        vertexdealloc(m, newvertex);
+      } else if (success == VIOLATINGVERTEX) {
+        /* Failed to insert the new vertex, but some subsegment was */
+        /*   marked as being encroached.                            */
+        vertexdealloc(m, newvertex);
+      } else {                                 /* success == DUPLICATEVERTEX */
+        /* Couldn't insert the new vertex because a vertex is already there. */
+        if (!b->quiet) {
+          printf(
+            "Warning:  New vertex (%.12g, %.12g) falls on existing vertex.\n",
+                 newvertex[0], newvertex[1]);
+          errorflag = 1;
+        }
+        vertexdealloc(m, newvertex);
+      }
+    }
+    if (errorflag) {
+      if (b->verbose) {
+        printf("  The new vertex is at the circumcenter of triangle\n");
+        printf("    (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+               borg[0], borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
+      }
+      printf("This probably means that I am trying to refine triangles\n");
+      printf("  to a smaller size than can be accommodated by the finite\n");
+      printf("  precision of floating point arithmetic.  (You can be\n");
+      printf("  sure of this if I fail to terminate.)\n");
+      precisionerror();
+    }
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  enforcequality()   Remove all the encroached subsegments and bad         */
+/*                     triangles from the triangulation.                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enforcequality(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void enforcequality(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badtriang *badtri;
+  int i;
+
+  if (!b->quiet) {
+    printf("Adding Steiner points to enforce quality.\n");
+  }
+  /* Initialize the pool of encroached subsegments. */
+  poolinit(&m->badsubsegs, sizeof(struct badsubseg), BADSUBSEGPERBLOCK,
+           BADSUBSEGPERBLOCK, POINTER, 0);
+  if (b->verbose) {
+    printf("  Looking for encroached subsegments.\n");
+  }
+  /* Test all segments to see if they're encroached. */
+  tallyencs(m, b);
+  if (b->verbose && (m->badsubsegs.items > 0)) {
+    printf("  Splitting encroached subsegments.\n");
+  }
+  /* Fix encroached subsegments without noting bad triangles. */
+  splitencsegs(m, b, 0);
+  /* At this point, if we haven't run out of Steiner points, the */
+  /*   triangulation should be (conforming) Delaunay.            */
+
+  /* Next, we worry about enforcing triangle quality. */
+  if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
+    /* Initialize the pool of bad triangles. */
+    poolinit(&m->badtriangles, sizeof(struct badtriang), BADTRIPERBLOCK,
+             BADTRIPERBLOCK, POINTER, 0);
+    /* Initialize the queues of bad triangles. */
+    for (i = 0; i < 64; i++) {
+      m->queuefront[i] = (struct badtriang *) NULL;
+    }
+    m->firstnonemptyq = -1;
+    /* Test all triangles to see if they're bad. */
+    tallyfaces(m, b);
+    /* Initialize the pool of recently flipped triangles. */
+    poolinit(&m->flipstackers, sizeof(struct flipstacker), FLIPSTACKERPERBLOCK,
+             FLIPSTACKERPERBLOCK, POINTER, 0);
+    m->checkquality = 1;
+    if (b->verbose) {
+      printf("  Splitting bad triangles.\n");
+    }
+    while ((m->badtriangles.items > 0) && (m->steinerleft != 0)) {
+      /* Fix one bad triangle by inserting a vertex at its circumcenter. */
+      badtri = dequeuebadtriang(m);
+      splittriangle(m, b, badtri);
+      if (m->badsubsegs.items > 0) {
+        /* Put bad triangle back in queue for another try later. */
+        enqueuebadtriang(m, b, badtri);
+        /* Fix any encroached subsegments that resulted. */
+        /*   Record any new bad triangles that result.   */
+        splitencsegs(m, b, 1);
+      } else {
+        /* Return the bad triangle to the pool. */
+        pooldealloc(&m->badtriangles, (VOID *) badtri);
+      }
+    }
+  }
+  /* At this point, if we haven't run out of Steiner points, the */
+  /*   triangulation should be (conforming) Delaunay and have no */
+  /*   low-quality triangles.                                    */
+
+  /* Might we have run out of Steiner points too soon? */
+  if (!b->quiet && (m->badsubsegs.items > 0) && (m->steinerleft == 0)) {
+    printf("\nWarning:  I ran out of Steiner points, but the mesh has\n");
+    if (m->badsubsegs.items == 1) {
+      printf("  an encroached subsegment, and therefore might not be truly\n");
+    } else {
+      printf("  %ld encroached subsegments, and therefore might not be truly\n"
+             , m->badsubsegs.items);
+    }
+    printf("  Delaunay.  If the Delaunay property is important to you,\n");
+    printf("  try increasing the number of Steiner points (controlled by\n");
+    printf("  the -S switch) slightly and try again.\n\n");
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh quality maintenance ends here                        *********/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  highorder()   Create extra nodes for quadratic subparametric elements.   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void highorder(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void highorder(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop, trisym;
+  struct osub checkmark;
+  vertex newvertex;
+  vertex torg, tdest;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (!b->quiet) {
+    printf("Adding vertices for second-order triangles.\n");
+  }
+  /* The following line ensures that dead items in the pool of nodes    */
+  /*   cannot be allocated for the extra nodes associated with high     */
+  /*   order elements.  This ensures that the primary nodes (at the     */
+  /*   corners of elements) will occur earlier in the output files, and */
+  /*   have lower indices, than the extra nodes.                        */
+  m->vertices.deaditemstack = (VOID *) NULL;
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  /* To loop over the set of edges, loop over all triangles, and look at   */
+  /*   the three edges of each triangle.  If there isn't another triangle  */
+  /*   adjacent to the edge, operate on the edge.  If there is another     */
+  /*   adjacent triangle, operate on the edge only if the current triangle */
+  /*   has a smaller pointer than its neighbor.  This way, each edge is    */
+  /*   considered only once.                                               */
+  while (triangleloop.tri != (triangle *) NULL) {
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      sym(triangleloop, trisym);
+      if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+        org(triangleloop, torg);
+        dest(triangleloop, tdest);
+        /* Create a new node in the middle of the edge.  Interpolate */
+        /*   its attributes.                                         */
+        newvertex = (vertex) poolalloc(&m->vertices);
+        for (i = 0; i < 2 + m->nextras; i++) {
+          newvertex[i] = 0.5 * (torg[i] + tdest[i]);
+        }
+        /* Set the new node's marker to zero or one, depending on */
+        /*   whether it lies on a boundary.                       */
+        setvertexmark(newvertex, trisym.tri == m->dummytri);
+        setvertextype(newvertex,
+                      trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX);
+        if (b->usesegments) {
+          tspivot(triangleloop, checkmark);
+          /* If this edge is a segment, transfer the marker to the new node. */
+          if (checkmark.ss != m->dummysub) {
+            setvertexmark(newvertex, mark(checkmark));
+            setvertextype(newvertex, SEGMENTVERTEX);
+          }
+        }
+        if (b->verbose > 1) {
+          printf("  Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+        }
+        /* Record the new node in the (one or two) adjacent elements. */
+        triangleloop.tri[m->highorderindex + triangleloop.orient] =
+                (triangle) newvertex;
+        if (trisym.tri != m->dummytri) {
+          trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+}
+
+/********* File I/O routines begin here                              *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  readline()   Read a nonempty line from a file.                           */
+/*                                                                           */
+/*  A line is considered "nonempty" if it contains something that looks like */
+/*  a number.  Comments (prefaced by `#') are ignored.                       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+char *readline(char *string, FILE *infile, char *infilename)
+#else /* not ANSI_DECLARATORS */
+char *readline(string, infile, infilename)
+char *string;
+FILE *infile;
+char *infilename;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  char *result;
+
+  /* Search for something that looks like a number. */
+  do {
+    result = fgets(string, INPUTLINESIZE, infile);
+    if (result == (char *) NULL) {
+      printf("  Error:  Unexpected end of file in %s.\n", infilename);
+      exit(1);
+    }
+    /* Skip anything that doesn't look like a number, a comment, */
+    /*   or the end of a line.                                   */
+    while ((*result != '\0') && (*result != '#')
+           && (*result != '.') && (*result != '+') && (*result != '-')
+           && ((*result < '0') || (*result > '9'))) {
+      result++;
+    }
+  /* If it's a comment or end of line, read another line and try again. */
+  } while ((*result == '#') || (*result == '\0'));
+  return result;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  findfield()   Find the next field of a string.                           */
+/*                                                                           */
+/*  Jumps past the current field by searching for whitespace, then jumps     */
+/*  past the whitespace to find the next field.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+char *findfield(char *string)
+#else /* not ANSI_DECLARATORS */
+char *findfield(string)
+char *string;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  char *result;
+
+  result = string;
+  /* Skip the current field.  Stop upon reaching whitespace. */
+  while ((*result != '\0') && (*result != '#')
+         && (*result != ' ') && (*result != '\t')) {
+    result++;
+  }
+  /* Now skip the whitespace and anything else that doesn't look like a */
+  /*   number, a comment, or the end of a line.                         */
+  while ((*result != '\0') && (*result != '#')
+         && (*result != '.') && (*result != '+') && (*result != '-')
+         && ((*result < '0') || (*result > '9'))) {
+    result++;
+  }
+  /* Check for a comment (prefixed with `#'). */
+  if (*result == '#') {
+    *result = '\0';
+  }
+  return result;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  readnodes()   Read the vertices from a file, which may be a .node or     */
+/*                .poly file.                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void readnodes(struct mesh *m, struct behavior *b, char *nodefilename,
+               char *polyfilename, FILE **polyfile)
+#else /* not ANSI_DECLARATORS */
+void readnodes(m, b, nodefilename, polyfilename, polyfile)
+struct mesh *m;
+struct behavior *b;
+char *nodefilename;
+char *polyfilename;
+FILE **polyfile;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  FILE *infile;
+  vertex vertexloop;
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  char *infilename;
+  REAL x, y;
+  int firstnode;
+  int nodemarkers;
+  int currentmarker;
+  int i, j;
+
+  if (b->poly) {
+    /* Read the vertices from a .poly file. */
+    if (!b->quiet) {
+      printf("Opening %s.\n", polyfilename);
+    }
+    *polyfile = fopen(polyfilename, "r");
+    if (*polyfile == (FILE *) NULL) {
+      printf("  Error:  Cannot access file %s.\n", polyfilename);
+      exit(1);
+    }
+    /* Read number of vertices, number of dimensions, number of vertex */
+    /*   attributes, and number of boundary markers.                   */
+    stringptr = readline(inputline, *polyfile, polyfilename);
+    m->invertices = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->mesh_dim = 2;
+    } else {
+      m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->nextras = 0;
+    } else {
+      m->nextras = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      nodemarkers = 0;
+    } else {
+      nodemarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+    if (m->invertices > 0) {
+      infile = *polyfile;
+      infilename = polyfilename;
+      m->readnodefile = 0;
+    } else {
+      /* If the .poly file claims there are zero vertices, that means that */
+      /*   the vertices should be read from a separate .node file.         */
+      m->readnodefile = 1;
+      infilename = nodefilename;
+    }
+  } else {
+    m->readnodefile = 1;
+    infilename = nodefilename;
+    *polyfile = (FILE *) NULL;
+  }
+
+  if (m->readnodefile) {
+    /* Read the vertices from a .node file. */
+    if (!b->quiet) {
+      printf("Opening %s.\n", nodefilename);
+    }
+    infile = fopen(nodefilename, "r");
+    if (infile == (FILE *) NULL) {
+      printf("  Error:  Cannot access file %s.\n", nodefilename);
+      exit(1);
+    }
+    /* Read number of vertices, number of dimensions, number of vertex */
+    /*   attributes, and number of boundary markers.                   */
+    stringptr = readline(inputline, infile, nodefilename);
+    m->invertices = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->mesh_dim = 2;
+    } else {
+      m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->nextras = 0;
+    } else {
+      m->nextras = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      nodemarkers = 0;
+    } else {
+      nodemarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+  }
+
+  if (m->invertices < 3) {
+    printf("Error:  Input must have at least three input vertices.\n");
+    exit(1);
+  }
+  if (m->mesh_dim != 2) {
+    printf("Error:  Triangle only works with two-dimensional meshes.\n");
+    exit(1);
+  }
+  if (m->nextras == 0) {
+    b->weighted = 0;
+  }
+
+  initializevertexpool(m, b);
+
+  /* Read the vertices. */
+  for (i = 0; i < m->invertices; i++) {
+    vertexloop = (vertex) poolalloc(&m->vertices);
+    stringptr = readline(inputline, infile, infilename);
+    if (i == 0) {
+      firstnode = (int) strtol(stringptr, &stringptr, 0);
+      if ((firstnode == 0) || (firstnode == 1)) {
+        b->firstnumber = firstnode;
+      }
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Vertex %d has no x coordinate.\n", b->firstnumber + i);
+      exit(1);
+    }
+    x = (REAL) strtod(stringptr, &stringptr);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Vertex %d has no y coordinate.\n", b->firstnumber + i);
+      exit(1);
+    }
+    y = (REAL) strtod(stringptr, &stringptr);
+    vertexloop[0] = x;
+    vertexloop[1] = y;
+    /* Read the vertex attributes. */
+    for (j = 2; j < 2 + m->nextras; j++) {
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        vertexloop[j] = 0.0;
+      } else {
+        vertexloop[j] = (REAL) strtod(stringptr, &stringptr);
+      }
+    }
+    if (nodemarkers) {
+      /* Read a vertex marker. */
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        setvertexmark(vertexloop, 0);
+      } else {
+        currentmarker = (int) strtol(stringptr, &stringptr, 0);
+        setvertexmark(vertexloop, currentmarker);
+      }
+    } else {
+      /* If no markers are specified in the file, they default to zero. */
+      setvertexmark(vertexloop, 0);
+    }
+    setvertextype(vertexloop, INPUTVERTEX);
+    /* Determine the smallest and largest x and y coordinates. */
+    if (i == 0) {
+      m->xmin = m->xmax = x;
+      m->ymin = m->ymax = y;
+    } else {
+      m->xmin = (x < m->xmin) ? x : m->xmin;
+      m->xmax = (x > m->xmax) ? x : m->xmax;
+      m->ymin = (y < m->ymin) ? y : m->ymin;
+      m->ymax = (y > m->ymax) ? y : m->ymax;
+    }
+  }
+  if (m->readnodefile) {
+    fclose(infile);
+  }
+
+  /* Nonexistent x value used as a flag to mark circle events in sweepline */
+  /*   Delaunay algorithm.                                                 */
+  m->xminextreme = 10 * m->xmin - 9 * m->xmax;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  transfernodes()   Read the vertices from memory.                         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist,
+                   REAL *pointattriblist, int *pointmarkerlist,
+                   int numberofpoints, int numberofpointattribs)
+#else /* not ANSI_DECLARATORS */
+void transfernodes(m, b, pointlist, pointattriblist, pointmarkerlist,
+                   numberofpoints, numberofpointattribs)
+struct mesh *m;
+struct behavior *b;
+REAL *pointlist;
+REAL *pointattriblist;
+int *pointmarkerlist;
+int numberofpoints;
+int numberofpointattribs;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex vertexloop;
+  REAL x, y;
+  int i, j;
+  int coordindex;
+  int attribindex;
+
+  m->invertices = numberofpoints;
+  m->mesh_dim = 2;
+  m->nextras = numberofpointattribs;
+  m->readnodefile = 0;
+  if (m->invertices < 3) {
+    printf("Error:  Input must have at least three input vertices.\n");
+    exit(1);
+  }
+  if (m->nextras == 0) {
+    b->weighted = 0;
+  }
+
+  initializevertexpool(m, b);
+
+  /* Read the vertices. */
+  coordindex = 0;
+  attribindex = 0;
+  for (i = 0; i < m->invertices; i++) {
+    vertexloop = (vertex) poolalloc(&m->vertices);
+    /* Read the vertex coordinates. */
+    x = vertexloop[0] = pointlist[coordindex++];
+    y = vertexloop[1] = pointlist[coordindex++];
+    /* Read the vertex attributes. */
+    for (j = 0; j < numberofpointattribs; j++) {
+      vertexloop[2 + j] = pointattriblist[attribindex++];
+    }
+    if (pointmarkerlist != (int *) NULL) {
+      /* Read a vertex marker. */
+      setvertexmark(vertexloop, pointmarkerlist[i]);
+    } else {
+      /* If no markers are specified, they default to zero. */
+      setvertexmark(vertexloop, 0);
+    }
+    setvertextype(vertexloop, INPUTVERTEX);
+    /* Determine the smallest and largest x and y coordinates. */
+    if (i == 0) {
+      m->xmin = m->xmax = x;
+      m->ymin = m->ymax = y;
+    } else {
+      m->xmin = (x < m->xmin) ? x : m->xmin;
+      m->xmax = (x > m->xmax) ? x : m->xmax;
+      m->ymin = (y < m->ymin) ? y : m->ymin;
+      m->ymax = (y > m->ymax) ? y : m->ymax;
+    }
+  }
+
+  /* Nonexistent x value used as a flag to mark circle events in sweepline */
+  /*   Delaunay algorithm.                                                 */
+  m->xminextreme = 10 * m->xmin - 9 * m->xmax;
+}
+
+#endif /* TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  readholes()   Read the holes, and possibly regional attributes and area  */
+/*                constraints, from a .poly file.                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void readholes(struct mesh *m, struct behavior *b,
+               FILE *polyfile, char *polyfilename, REAL **hlist, int *holes,
+               REAL **rlist, int *regions)
+#else /* not ANSI_DECLARATORS */
+void readholes(m, b, polyfile, polyfilename, hlist, holes, rlist, regions)
+struct mesh *m;
+struct behavior *b;
+FILE *polyfile;
+char *polyfilename;
+REAL **hlist;
+int *holes;
+REAL **rlist;
+int *regions;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL *holelist;
+  REAL *regionlist;
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  int index;
+  int i;
+
+  /* Read the holes. */
+  stringptr = readline(inputline, polyfile, polyfilename);
+  *holes = (int) strtol(stringptr, &stringptr, 0);
+  if (*holes > 0) {
+    holelist = (REAL *) trimalloc(2 * *holes * (int) sizeof(REAL));
+    *hlist = holelist;
+    for (i = 0; i < 2 * *holes; i += 2) {
+      stringptr = readline(inputline, polyfile, polyfilename);
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no x coordinate.\n",
+               b->firstnumber + (i >> 1));
+        exit(1);
+      } else {
+        holelist[i] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no y coordinate.\n",
+               b->firstnumber + (i >> 1));
+        exit(1);
+      } else {
+        holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
+      }
+    }
+  } else {
+    *hlist = (REAL *) NULL;
+  }
+
+#ifndef CDT_ONLY
+  if ((b->regionattrib || b->vararea) && !b->refine) {
+    /* Read the area constraints. */
+    stringptr = readline(inputline, polyfile, polyfilename);
+    *regions = (int) strtol(stringptr, &stringptr, 0);
+    if (*regions > 0) {
+      regionlist = (REAL *) trimalloc(4 * *regions * (int) sizeof(REAL));
+      *rlist = regionlist;
+      index = 0;
+      for (i = 0; i < *regions; i++) {
+        stringptr = readline(inputline, polyfile, polyfilename);
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Region %d has no x coordinate.\n",
+                 b->firstnumber + i);
+          exit(1);
+        } else {
+          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+        }
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Region %d has no y coordinate.\n",
+                 b->firstnumber + i);
+          exit(1);
+        } else {
+          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+        }
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          printf(
+            "Error:  Region %d has no region attribute or area constraint.\n",
+                 b->firstnumber + i);
+          exit(1);
+        } else {
+          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+        }
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          regionlist[index] = regionlist[index - 1];
+        } else {
+          regionlist[index] = (REAL) strtod(stringptr, &stringptr);
+        }
+        index++;
+      }
+    }
+  } else {
+    /* Set `*regions' to zero to avoid an accidental free() later. */
+    *regions = 0;
+    *rlist = (REAL *) NULL;
+  }
+#endif /* not CDT_ONLY */
+
+  fclose(polyfile);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  finishfile()   Write the command line to the output file so the user     */
+/*                 can remember how the file was generated.  Close the file. */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void finishfile(FILE *outfile, int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void finishfile(outfile, argc, argv)
+FILE *outfile;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int i;
+
+  fprintf(outfile, "# Generated by");
+  for (i = 0; i < argc; i++) {
+    fprintf(outfile, " ");
+    fputs(argv[i], outfile);
+  }
+  fprintf(outfile, "\n");
+  fclose(outfile);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writenodes()   Number the vertices and write them to a .node file.       */
+/*                                                                           */
+/*  To save memory, the vertex numbers are written over the boundary markers */
+/*  after the vertices are written to a file.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist,
+                REAL **pointattriblist, int **pointmarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writenodes(m, b, pointlist, pointattriblist, pointmarkerlist)
+struct mesh *m;
+struct behavior *b;
+REAL **pointlist;
+REAL **pointattriblist;
+int **pointmarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writenodes(struct mesh *m, struct behavior *b, char *nodefilename,
+                int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writenodes(m, b, nodefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *nodefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  REAL *plist;
+  REAL *palist;
+  int *pmlist;
+  int coordindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  vertex vertexloop;
+  long outvertices;
+  int vertexnumber;
+  int i;
+
+  if (b->jettison) {
+    outvertices = m->vertices.items - m->undeads;
+  } else {
+    outvertices = m->vertices.items;
+  }
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing vertices.\n");
+  }
+  /* Allocate memory for output vertices if necessary. */
+  if (*pointlist == (REAL *) NULL) {
+    *pointlist = (REAL *) trimalloc(outvertices * 2 * sizeof(REAL));
+  }
+  /* Allocate memory for output vertex attributes if necessary. */
+  if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) {
+    *pointattriblist = (REAL *) trimalloc(outvertices * m->nextras *
+                                          sizeof(REAL));
+  }
+  /* Allocate memory for output vertex markers if necessary. */
+  if (!b->nobound && (*pointmarkerlist == (int *) NULL)) {
+    *pointmarkerlist = (int *) trimalloc(outvertices * sizeof(int));
+  }
+  plist = *pointlist;
+  palist = *pointattriblist;
+  pmlist = *pointmarkerlist;
+  coordindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", nodefilename);
+  }
+  outfile = fopen(nodefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", nodefilename);
+    exit(1);
+  }
+  /* Number of vertices, number of dimensions, number of vertex attributes, */
+  /*   and number of boundary markers (zero or one).                        */
+  fprintf(outfile, "%ld  %d  %d  %d\n", outvertices, m->mesh_dim,
+          m->nextras, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->vertices);
+  vertexnumber = b->firstnumber;
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+#ifdef TRILIBRARY
+      /* X and y coordinates. */
+      plist[coordindex++] = vertexloop[0];
+      plist[coordindex++] = vertexloop[1];
+      /* Vertex attributes. */
+      for (i = 0; i < m->nextras; i++) {
+        palist[attribindex++] = vertexloop[2 + i];
+      }
+      if (!b->nobound) {
+        /* Copy the boundary marker. */
+        pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop);
+      }
+#else /* not TRILIBRARY */
+      /* Vertex number, x and y coordinates. */
+      fprintf(outfile, "%4d    %.17g  %.17g", vertexnumber, vertexloop[0],
+              vertexloop[1]);
+      for (i = 0; i < m->nextras; i++) {
+        /* Write an attribute. */
+        fprintf(outfile, "  %.17g", vertexloop[i + 2]);
+      }
+      if (b->nobound) {
+        fprintf(outfile, "\n");
+      } else {
+        /* Write the boundary marker. */
+        fprintf(outfile, "    %d\n", vertexmark(vertexloop));
+      }
+#endif /* not TRILIBRARY */
+
+      setvertexmark(vertexloop, vertexnumber);
+      vertexnumber++;
+    }
+    vertexloop = vertextraverse(m);
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  numbernodes()   Number the vertices.                                     */
+/*                                                                           */
+/*  Each vertex is assigned a marker equal to its number.                    */
+/*                                                                           */
+/*  Used when writenodes() is not called because no .node file is written.   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void numbernodes(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void numbernodes(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex vertexloop;
+  int vertexnumber;
+
+  traversalinit(&m->vertices);
+  vertexnumber = b->firstnumber;
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    setvertexmark(vertexloop, vertexnumber);
+    if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+      vertexnumber++;
+    }
+    vertexloop = vertextraverse(m);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writeelements()   Write the triangles to an .ele file.                   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeelements(struct mesh *m, struct behavior *b,
+                   int **trianglelist, REAL **triangleattriblist)
+#else /* not ANSI_DECLARATORS */
+void writeelements(m, b, trianglelist, triangleattriblist)
+struct mesh *m;
+struct behavior *b;
+int **trianglelist;
+REAL **triangleattriblist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeelements(struct mesh *m, struct behavior *b, char *elefilename,
+                   int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeelements(m, b, elefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *elefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *tlist;
+  REAL *talist;
+  int vertexindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop;
+  vertex p1, p2, p3;
+  vertex mid1, mid2, mid3;
+  long elementnumber;
+  int i;
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing triangles.\n");
+  }
+  /* Allocate memory for output triangles if necessary. */
+  if (*trianglelist == (int *) NULL) {
+    *trianglelist = (int *) trimalloc(m->triangles.items *
+                                      ((b->order + 1) * (b->order + 2) / 2) *
+                                      sizeof(int));
+  }
+  /* Allocate memory for output triangle attributes if necessary. */
+  if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) {
+    *triangleattriblist = (REAL *) trimalloc(m->triangles.items * m->eextras *
+                                             sizeof(REAL));
+  }
+  tlist = *trianglelist;
+  talist = *triangleattriblist;
+  vertexindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", elefilename);
+  }
+  outfile = fopen(elefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", elefilename);
+    exit(1);
+  }
+  /* Number of triangles, vertices per triangle, attributes per triangle. */
+  fprintf(outfile, "%ld  %d  %d\n", m->triangles.items,
+          (b->order + 1) * (b->order + 2) / 2, m->eextras);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, p1);
+    dest(triangleloop, p2);
+    apex(triangleloop, p3);
+    if (b->order == 1) {
+#ifdef TRILIBRARY
+      tlist[vertexindex++] = vertexmark(p1);
+      tlist[vertexindex++] = vertexmark(p2);
+      tlist[vertexindex++] = vertexmark(p3);
+#else /* not TRILIBRARY */
+      /* Triangle number, indices for three vertices. */
+      fprintf(outfile, "%4ld    %4d  %4d  %4d", elementnumber,
+              vertexmark(p1), vertexmark(p2), vertexmark(p3));
+#endif /* not TRILIBRARY */
+    } else {
+      mid1 = (vertex) triangleloop.tri[m->highorderindex + 1];
+      mid2 = (vertex) triangleloop.tri[m->highorderindex + 2];
+      mid3 = (vertex) triangleloop.tri[m->highorderindex];
+#ifdef TRILIBRARY
+      tlist[vertexindex++] = vertexmark(p1);
+      tlist[vertexindex++] = vertexmark(p2);
+      tlist[vertexindex++] = vertexmark(p3);
+      tlist[vertexindex++] = vertexmark(mid1);
+      tlist[vertexindex++] = vertexmark(mid2);
+      tlist[vertexindex++] = vertexmark(mid3);
+#else /* not TRILIBRARY */
+      /* Triangle number, indices for six vertices. */
+      fprintf(outfile, "%4ld    %4d  %4d  %4d  %4d  %4d  %4d", elementnumber,
+              vertexmark(p1), vertexmark(p2), vertexmark(p3), vertexmark(mid1),
+              vertexmark(mid2), vertexmark(mid3));
+#endif /* not TRILIBRARY */
+    }
+
+#ifdef TRILIBRARY
+    for (i = 0; i < m->eextras; i++) {
+      talist[attribindex++] = elemattribute(triangleloop, i);
+    }
+#else /* not TRILIBRARY */
+    for (i = 0; i < m->eextras; i++) {
+      fprintf(outfile, "  %.17g", elemattribute(triangleloop, i));
+    }
+    fprintf(outfile, "\n");
+#endif /* not TRILIBRARY */
+
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writepoly()   Write the segments and holes to a .poly file.              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writepoly(struct mesh *m, struct behavior *b,
+               int **segmentlist, int **segmentmarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writepoly(m, b, segmentlist, segmentmarkerlist)
+struct mesh *m;
+struct behavior *b;
+int **segmentlist;
+int **segmentmarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writepoly(struct mesh *m, struct behavior *b, char *polyfilename,
+               REAL *holelist, int holes, REAL *regionlist, int regions,
+               int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writepoly(m, b, polyfilename, holelist, holes, regionlist, regions,
+               argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *polyfilename;
+REAL *holelist;
+int holes;
+REAL *regionlist;
+int regions;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *slist;
+  int *smlist;
+  int index;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+  long holenumber, regionnumber;
+#endif /* not TRILIBRARY */
+  struct osub subsegloop;
+  vertex endpoint1, endpoint2;
+  long subsegnumber;
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing segments.\n");
+  }
+  /* Allocate memory for output segments if necessary. */
+  if (*segmentlist == (int *) NULL) {
+    *segmentlist = (int *) trimalloc(m->subsegs.items * 2 * sizeof(int));
+  }
+  /* Allocate memory for output segment markers if necessary. */
+  if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) {
+    *segmentmarkerlist = (int *) trimalloc(m->subsegs.items * sizeof(int));
+  }
+  slist = *segmentlist;
+  smlist = *segmentmarkerlist;
+  index = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", polyfilename);
+  }
+  outfile = fopen(polyfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", polyfilename);
+    exit(1);
+  }
+  /* The zero indicates that the vertices are in a separate .node file. */
+  /*   Followed by number of dimensions, number of vertex attributes,   */
+  /*   and number of boundary markers (zero or one).                    */
+  fprintf(outfile, "%d  %d  %d  %d\n", 0, m->mesh_dim, m->nextras,
+          1 - b->nobound);
+  /* Number of segments, number of boundary markers (zero or one). */
+  fprintf(outfile, "%ld  %d\n", m->subsegs.items, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->subsegs);
+  subsegloop.ss = subsegtraverse(m);
+  subsegloop.ssorient = 0;
+  subsegnumber = b->firstnumber;
+  while (subsegloop.ss != (subseg *) NULL) {
+    sorg(subsegloop, endpoint1);
+    sdest(subsegloop, endpoint2);
+#ifdef TRILIBRARY
+    /* Copy indices of the segment's two endpoints. */
+    slist[index++] = vertexmark(endpoint1);
+    slist[index++] = vertexmark(endpoint2);
+    if (!b->nobound) {
+      /* Copy the boundary marker. */
+      smlist[subsegnumber - b->firstnumber] = mark(subsegloop);
+    }
+#else /* not TRILIBRARY */
+    /* Segment number, indices of its two endpoints, and possibly a marker. */
+    if (b->nobound) {
+      fprintf(outfile, "%4ld    %4d  %4d\n", subsegnumber,
+              vertexmark(endpoint1), vertexmark(endpoint2));
+    } else {
+      fprintf(outfile, "%4ld    %4d  %4d    %4d\n", subsegnumber,
+              vertexmark(endpoint1), vertexmark(endpoint2), mark(subsegloop));
+    }
+#endif /* not TRILIBRARY */
+
+    subsegloop.ss = subsegtraverse(m);
+    subsegnumber++;
+  }
+
+#ifndef TRILIBRARY
+#ifndef CDT_ONLY
+  fprintf(outfile, "%d\n", holes);
+  if (holes > 0) {
+    for (holenumber = 0; holenumber < holes; holenumber++) {
+      /* Hole number, x and y coordinates. */
+      fprintf(outfile, "%4ld   %.17g  %.17g\n", b->firstnumber + holenumber,
+              holelist[2 * holenumber], holelist[2 * holenumber + 1]);
+    }
+  }
+  if (regions > 0) {
+    fprintf(outfile, "%d\n", regions);
+    for (regionnumber = 0; regionnumber < regions; regionnumber++) {
+      /* Region number, x and y coordinates, attribute, maximum area. */
+      fprintf(outfile, "%4ld   %.17g  %.17g  %.17g  %.17g\n",
+              b->firstnumber + regionnumber,
+              regionlist[4 * regionnumber], regionlist[4 * regionnumber + 1],
+              regionlist[4 * regionnumber + 2],
+              regionlist[4 * regionnumber + 3]);
+    }
+  }
+#endif /* not CDT_ONLY */
+
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writeedges()   Write the edges to an .edge file.                         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeedges(struct mesh *m, struct behavior *b,
+                int **edgelist, int **edgemarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writeedges(m, b, edgelist, edgemarkerlist)
+struct mesh *m;
+struct behavior *b;
+int **edgelist;
+int **edgemarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeedges(struct mesh *m, struct behavior *b, char *edgefilename,
+                int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeedges(m, b, edgefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *edgefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *elist;
+  int *emlist;
+  int index;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop, trisym;
+  struct osub checkmark;
+  vertex p1, p2;
+  long edgenumber;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing edges.\n");
+  }
+  /* Allocate memory for edges if necessary. */
+  if (*edgelist == (int *) NULL) {
+    *edgelist = (int *) trimalloc(m->edges * 2 * sizeof(int));
+  }
+  /* Allocate memory for edge markers if necessary. */
+  if (!b->nobound && (*edgemarkerlist == (int *) NULL)) {
+    *edgemarkerlist = (int *) trimalloc(m->edges * sizeof(int));
+  }
+  elist = *edgelist;
+  emlist = *edgemarkerlist;
+  index = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", edgefilename);
+  }
+  outfile = fopen(edgefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", edgefilename);
+    exit(1);
+  }
+  /* Number of edges, number of boundary markers (zero or one). */
+  fprintf(outfile, "%ld  %d\n", m->edges, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  edgenumber = b->firstnumber;
+  /* To loop over the set of edges, loop over all triangles, and look at   */
+  /*   the three edges of each triangle.  If there isn't another triangle  */
+  /*   adjacent to the edge, operate on the edge.  If there is another     */
+  /*   adjacent triangle, operate on the edge only if the current triangle */
+  /*   has a smaller pointer than its neighbor.  This way, each edge is    */
+  /*   considered only once.                                               */
+  while (triangleloop.tri != (triangle *) NULL) {
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      sym(triangleloop, trisym);
+      if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+        org(triangleloop, p1);
+        dest(triangleloop, p2);
+#ifdef TRILIBRARY
+        elist[index++] = vertexmark(p1);
+        elist[index++] = vertexmark(p2);
+#endif /* TRILIBRARY */
+        if (b->nobound) {
+#ifndef TRILIBRARY
+          /* Edge number, indices of two endpoints. */
+          fprintf(outfile, "%4ld   %d  %d\n", edgenumber,
+                  vertexmark(p1), vertexmark(p2));
+#endif /* not TRILIBRARY */
+        } else {
+          /* Edge number, indices of two endpoints, and a boundary marker. */
+          /*   If there's no subsegment, the boundary marker is zero.      */
+          if (b->usesegments) {
+            tspivot(triangleloop, checkmark);
+            if (checkmark.ss == m->dummysub) {
+#ifdef TRILIBRARY
+              emlist[edgenumber - b->firstnumber] = 0;
+#else /* not TRILIBRARY */
+              fprintf(outfile, "%4ld   %d  %d  %d\n", edgenumber,
+                      vertexmark(p1), vertexmark(p2), 0);
+#endif /* not TRILIBRARY */
+            } else {
+#ifdef TRILIBRARY
+              emlist[edgenumber - b->firstnumber] = mark(checkmark);
+#else /* not TRILIBRARY */
+              fprintf(outfile, "%4ld   %d  %d  %d\n", edgenumber,
+                      vertexmark(p1), vertexmark(p2), mark(checkmark));
+#endif /* not TRILIBRARY */
+            }
+          } else {
+#ifdef TRILIBRARY
+            emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri;
+#else /* not TRILIBRARY */
+            fprintf(outfile, "%4ld   %d  %d  %d\n", edgenumber,
+                    vertexmark(p1), vertexmark(p2), trisym.tri == m->dummytri);
+#endif /* not TRILIBRARY */
+          }
+        }
+        edgenumber++;
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writevoronoi()   Write the Voronoi diagram to a .v.node and .v.edge      */
+/*                   file.                                                   */
+/*                                                                           */
+/*  The Voronoi diagram is the geometric dual of the Delaunay triangulation. */
+/*  Hence, the Voronoi vertices are listed by traversing the Delaunay        */
+/*  triangles, and the Voronoi edges are listed by traversing the Delaunay   */
+/*  edges.                                                                   */
+/*                                                                           */
+/*  WARNING:  In order to assign numbers to the Voronoi vertices, this       */
+/*  procedure messes up the subsegments or the extra nodes of every          */
+/*  element.  Hence, you should call this procedure last.                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist,
+                  REAL **vpointattriblist, int **vpointmarkerlist,
+                  int **vedgelist, int **vedgemarkerlist, REAL **vnormlist)
+#else /* not ANSI_DECLARATORS */
+void writevoronoi(m, b, vpointlist, vpointattriblist, vpointmarkerlist,
+                  vedgelist, vedgemarkerlist, vnormlist)
+struct mesh *m;
+struct behavior *b;
+REAL **vpointlist;
+REAL **vpointattriblist;
+int **vpointmarkerlist;
+int **vedgelist;
+int **vedgemarkerlist;
+REAL **vnormlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writevoronoi(struct mesh *m, struct behavior *b, char *vnodefilename,
+                  char *vedgefilename, int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writevoronoi(m, b, vnodefilename, vedgefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *vnodefilename;
+char *vedgefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  REAL *plist;
+  REAL *palist;
+  int *elist;
+  REAL *normlist;
+  int coordindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop, trisym;
+  vertex torg, tdest, tapex;
+  REAL circumcenter[2];
+  REAL xi, eta;
+  REAL dum;
+  long vnodenumber, vedgenumber;
+  int p1, p2;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing Voronoi vertices.\n");
+  }
+  /* Allocate memory for Voronoi vertices if necessary. */
+  if (*vpointlist == (REAL *) NULL) {
+    *vpointlist = (REAL *) trimalloc(m->triangles.items * 2 * sizeof(REAL));
+  }
+  /* Allocate memory for Voronoi vertex attributes if necessary. */
+  if (*vpointattriblist == (REAL *) NULL) {
+    *vpointattriblist = (REAL *) trimalloc(m->triangles.items * m->nextras *
+                                           sizeof(REAL));
+  }
+  *vpointmarkerlist = (int *) NULL;
+  plist = *vpointlist;
+  palist = *vpointattriblist;
+  coordindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", vnodefilename);
+  }
+  outfile = fopen(vnodefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", vnodefilename);
+    exit(1);
+  }
+  /* Number of triangles, two dimensions, number of vertex attributes, */
+  /*   no markers.                                                     */
+  fprintf(outfile, "%ld  %d  %d  %d\n", m->triangles.items, 2, m->nextras, 0);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  vnodenumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, torg);
+    dest(triangleloop, tdest);
+    apex(triangleloop, tapex);
+    findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, &dum,
+                     0);
+#ifdef TRILIBRARY
+    /* X and y coordinates. */
+    plist[coordindex++] = circumcenter[0];
+    plist[coordindex++] = circumcenter[1];
+    for (i = 2; i < 2 + m->nextras; i++) {
+      /* Interpolate the vertex attributes at the circumcenter. */
+      palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i])
+                                     + eta * (tapex[i] - torg[i]);
+    }
+#else /* not TRILIBRARY */
+    /* Voronoi vertex number, x and y coordinates. */
+    fprintf(outfile, "%4ld    %.17g  %.17g", vnodenumber, circumcenter[0],
+            circumcenter[1]);
+    for (i = 2; i < 2 + m->nextras; i++) {
+      /* Interpolate the vertex attributes at the circumcenter. */
+      fprintf(outfile, "  %.17g", torg[i] + xi * (tdest[i] - torg[i])
+                                         + eta * (tapex[i] - torg[i]));
+    }
+    fprintf(outfile, "\n");
+#endif /* not TRILIBRARY */
+
+    * (int *) (triangleloop.tri + 6) = (int) vnodenumber;
+    triangleloop.tri = triangletraverse(m);
+    vnodenumber++;
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing Voronoi edges.\n");
+  }
+  /* Allocate memory for output Voronoi edges if necessary. */
+  if (*vedgelist == (int *) NULL) {
+    *vedgelist = (int *) trimalloc(m->edges * 2 * sizeof(int));
+  }
+  *vedgemarkerlist = (int *) NULL;
+  /* Allocate memory for output Voronoi norms if necessary. */
+  if (*vnormlist == (REAL *) NULL) {
+    *vnormlist = (REAL *) trimalloc(m->edges * 2 * sizeof(REAL));
+  }
+  elist = *vedgelist;
+  normlist = *vnormlist;
+  coordindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", vedgefilename);
+  }
+  outfile = fopen(vedgefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", vedgefilename);
+    exit(1);
+  }
+  /* Number of edges, zero boundary markers. */
+  fprintf(outfile, "%ld  %d\n", m->edges, 0);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  vedgenumber = b->firstnumber;
+  /* To loop over the set of edges, loop over all triangles, and look at   */
+  /*   the three edges of each triangle.  If there isn't another triangle  */
+  /*   adjacent to the edge, operate on the edge.  If there is another     */
+  /*   adjacent triangle, operate on the edge only if the current triangle */
+  /*   has a smaller pointer than its neighbor.  This way, each edge is    */
+  /*   considered only once.                                               */
+  while (triangleloop.tri != (triangle *) NULL) {
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      sym(triangleloop, trisym);
+      if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+        /* Find the number of this triangle (and Voronoi vertex). */
+        p1 = * (int *) (triangleloop.tri + 6);
+        if (trisym.tri == m->dummytri) {
+          org(triangleloop, torg);
+          dest(triangleloop, tdest);
+#ifdef TRILIBRARY
+          /* Copy an infinite ray.  Index of one endpoint, and -1. */
+          elist[coordindex] = p1;
+          normlist[coordindex++] = tdest[1] - torg[1];
+          elist[coordindex] = -1;
+          normlist[coordindex++] = torg[0] - tdest[0];
+#else /* not TRILIBRARY */
+          /* Write an infinite ray.  Edge number, index of one endpoint, -1, */
+          /*   and x and y coordinates of a vector representing the          */
+          /*   direction of the ray.                                         */
+          fprintf(outfile, "%4ld   %d  %d   %.17g  %.17g\n", vedgenumber,
+                  p1, -1, tdest[1] - torg[1], torg[0] - tdest[0]);
+#endif /* not TRILIBRARY */
+        } else {
+          /* Find the number of the adjacent triangle (and Voronoi vertex). */
+          p2 = * (int *) (trisym.tri + 6);
+          /* Finite edge.  Write indices of two endpoints. */
+#ifdef TRILIBRARY
+          elist[coordindex] = p1;
+          normlist[coordindex++] = 0.0;
+          elist[coordindex] = p2;
+          normlist[coordindex++] = 0.0;
+#else /* not TRILIBRARY */
+          fprintf(outfile, "%4ld   %d  %d\n", vedgenumber, p1, p2);
+#endif /* not TRILIBRARY */
+        }
+        vedgenumber++;
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist)
+#else /* not ANSI_DECLARATORS */
+void writeneighbors(m, b, neighborlist)
+struct mesh *m;
+struct behavior *b;
+int **neighborlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeneighbors(struct mesh *m, struct behavior *b, char *neighborfilename,
+                    int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeneighbors(m, b, neighborfilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *neighborfilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *nlist;
+  int index;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop, trisym;
+  long elementnumber;
+  int neighbor1, neighbor2, neighbor3;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing neighbors.\n");
+  }
+  /* Allocate memory for neighbors if necessary. */
+  if (*neighborlist == (int *) NULL) {
+    *neighborlist = (int *) trimalloc(m->triangles.items * 3 * sizeof(int));
+  }
+  nlist = *neighborlist;
+  index = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", neighborfilename);
+  }
+  outfile = fopen(neighborfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", neighborfilename);
+    exit(1);
+  }
+  /* Number of triangles, three neighbors per triangle. */
+  fprintf(outfile, "%ld  %d\n", m->triangles.items, 3);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    * (int *) (triangleloop.tri + 6) = (int) elementnumber;
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+  * (int *) (m->dummytri + 6) = -1;
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    triangleloop.orient = 1;
+    sym(triangleloop, trisym);
+    neighbor1 = * (int *) (trisym.tri + 6);
+    triangleloop.orient = 2;
+    sym(triangleloop, trisym);
+    neighbor2 = * (int *) (trisym.tri + 6);
+    triangleloop.orient = 0;
+    sym(triangleloop, trisym);
+    neighbor3 = * (int *) (trisym.tri + 6);
+#ifdef TRILIBRARY
+    nlist[index++] = neighbor1;
+    nlist[index++] = neighbor2;
+    nlist[index++] = neighbor3;
+#else /* not TRILIBRARY */
+    /* Triangle number, neighboring triangle numbers. */
+    fprintf(outfile, "%4ld    %d  %d  %d\n", elementnumber,
+            neighbor1, neighbor2, neighbor3);
+#endif /* not TRILIBRARY */
+
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writeoff()   Write the triangulation to an .off file.                    */
+/*                                                                           */
+/*  OFF stands for the Object File Format, a format used by the Geometry     */
+/*  Center's Geomview package.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeoff(struct mesh *m, struct behavior *b, char *offfilename,
+              int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeoff(m, b, offfilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *offfilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  FILE *outfile;
+  struct otri triangleloop;
+  vertex vertexloop;
+  vertex p1, p2, p3;
+  long outvertices;
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", offfilename);
+  }
+
+  if (b->jettison) {
+    outvertices = m->vertices.items - m->undeads;
+  } else {
+    outvertices = m->vertices.items;
+  }
+
+  outfile = fopen(offfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", offfilename);
+    exit(1);
+  }
+  /* Number of vertices, triangles, and edges. */
+  fprintf(outfile, "OFF\n%ld  %ld  %ld\n", outvertices, m->triangles.items,
+          m->edges);
+
+  /* Write the vertices. */
+  traversalinit(&m->vertices);
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+      /* The "0.0" is here because the OFF format uses 3D coordinates. */
+      fprintf(outfile, " %.17g  %.17g  %.17g\n", vertexloop[0], vertexloop[1],
+              0.0);
+    }
+    vertexloop = vertextraverse(m);
+  }
+
+  /* Write the triangles. */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, p1);
+    dest(triangleloop, p2);
+    apex(triangleloop, p3);
+    /* The "3" means a three-vertex polygon. */
+    fprintf(outfile, " 3   %4d  %4d  %4d\n", vertexmark(p1) - 1,
+            vertexmark(p2) - 1, vertexmark(p3) - 1);
+    triangleloop.tri = triangletraverse(m);
+  }
+  finishfile(outfile, argc, argv);
+}
+
+#endif /* not TRILIBRARY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* File I/O routines end here                                *********/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  quality_statistics()   Print statistics about the quality of the mesh.   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void quality_statistics(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void quality_statistics(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  vertex p[3];
+  REAL cossquaretable[8];
+  REAL ratiotable[16];
+  REAL dx[3], dy[3];
+  REAL edgelength[3];
+  REAL dotproduct;
+  REAL cossquare;
+  REAL triarea;
+  REAL shortest, longest;
+  REAL trilongest2;
+  REAL smallestarea, biggestarea;
+  REAL triminaltitude2;
+  REAL minaltitude;
+  REAL triaspect2;
+  REAL worstaspect;
+  REAL smallestangle, biggestangle;
+  REAL radconst, degconst;
+  int angletable[18];
+  int aspecttable[16];
+  int aspectindex;
+  int tendegree;
+  int acutebiggest;
+  int i, ii, j, k;
+
+  printf("Mesh quality statistics:\n\n");
+  radconst = PI / 18.0;
+  degconst = 180.0 / PI;
+  for (i = 0; i < 8; i++) {
+    cossquaretable[i] = cos(radconst * (REAL) (i + 1));
+    cossquaretable[i] = cossquaretable[i] * cossquaretable[i];
+  }
+  for (i = 0; i < 18; i++) {
+    angletable[i] = 0;
+  }
+
+  ratiotable[0]  =      1.5;      ratiotable[1]  =     2.0;
+  ratiotable[2]  =      2.5;      ratiotable[3]  =     3.0;
+  ratiotable[4]  =      4.0;      ratiotable[5]  =     6.0;
+  ratiotable[6]  =     10.0;      ratiotable[7]  =    15.0;
+  ratiotable[8]  =     25.0;      ratiotable[9]  =    50.0;
+  ratiotable[10] =    100.0;      ratiotable[11] =   300.0;
+  ratiotable[12] =   1000.0;      ratiotable[13] = 10000.0;
+  ratiotable[14] = 100000.0;      ratiotable[15] =     0.0;
+  for (i = 0; i < 16; i++) {
+    aspecttable[i] = 0;
+  }
+
+  worstaspect = 0.0;
+  minaltitude = m->xmax - m->xmin + m->ymax - m->ymin;
+  minaltitude = minaltitude * minaltitude;
+  shortest = minaltitude;
+  longest = 0.0;
+  smallestarea = minaltitude;
+  biggestarea = 0.0;
+  worstaspect = 0.0;
+  smallestangle = 0.0;
+  biggestangle = 2.0;
+  acutebiggest = 1;
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, p[0]);
+    dest(triangleloop, p[1]);
+    apex(triangleloop, p[2]);
+    trilongest2 = 0.0;
+
+    for (i = 0; i < 3; i++) {
+      j = plus1mod3[i];
+      k = minus1mod3[i];
+      dx[i] = p[j][0] - p[k][0];
+      dy[i] = p[j][1] - p[k][1];
+      edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
+      if (edgelength[i] > trilongest2) {
+        trilongest2 = edgelength[i];
+      }
+      if (edgelength[i] > longest) {
+        longest = edgelength[i];
+      }
+      if (edgelength[i] < shortest) {
+        shortest = edgelength[i];
+      }
+    }
+
+    triarea = counterclockwise(m, b, p[0], p[1], p[2]);
+    if (triarea < smallestarea) {
+      smallestarea = triarea;
+    }
+    if (triarea > biggestarea) {
+      biggestarea = triarea;
+    }
+    triminaltitude2 = triarea * triarea / trilongest2;
+    if (triminaltitude2 < minaltitude) {
+      minaltitude = triminaltitude2;
+    }
+    triaspect2 = trilongest2 / triminaltitude2;
+    if (triaspect2 > worstaspect) {
+      worstaspect = triaspect2;
+    }
+    aspectindex = 0;
+    while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex])
+           && (aspectindex < 15)) {
+      aspectindex++;
+    }
+    aspecttable[aspectindex]++;
+
+    for (i = 0; i < 3; i++) {
+      j = plus1mod3[i];
+      k = minus1mod3[i];
+      dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
+      cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
+      tendegree = 8;
+      for (ii = 7; ii >= 0; ii--) {
+        if (cossquare > cossquaretable[ii]) {
+          tendegree = ii;
+        }
+      }
+      if (dotproduct <= 0.0) {
+        angletable[tendegree]++;
+        if (cossquare > smallestangle) {
+          smallestangle = cossquare;
+        }
+        if (acutebiggest && (cossquare < biggestangle)) {
+          biggestangle = cossquare;
+        }
+      } else {
+        angletable[17 - tendegree]++;
+        if (acutebiggest || (cossquare > biggestangle)) {
+          biggestangle = cossquare;
+          acutebiggest = 0;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+
+  shortest = sqrt(shortest);
+  longest = sqrt(longest);
+  minaltitude = sqrt(minaltitude);
+  worstaspect = sqrt(worstaspect);
+  smallestarea *= 0.5;
+  biggestarea *= 0.5;
+  if (smallestangle >= 1.0) {
+    smallestangle = 0.0;
+  } else {
+    smallestangle = degconst * acos(sqrt(smallestangle));
+  }
+  if (biggestangle >= 1.0) {
+    biggestangle = 180.0;
+  } else {
+    if (acutebiggest) {
+      biggestangle = degconst * acos(sqrt(biggestangle));
+    } else {
+      biggestangle = 180.0 - degconst * acos(sqrt(biggestangle));
+    }
+  }
+
+  printf("  Smallest area: %16.5g   |  Largest area: %16.5g\n",
+         smallestarea, biggestarea);
+  printf("  Shortest edge: %16.5g   |  Longest edge: %16.5g\n",
+         shortest, longest);
+  printf("  Shortest altitude: %12.5g   |  Largest aspect ratio: %8.5g\n\n",
+         minaltitude, worstaspect);
+
+  printf("  Triangle aspect ratio histogram:\n");
+  printf("  1.1547 - %-6.6g    :  %8d    | %6.6g - %-6.6g     :  %8d\n",
+         ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8],
+         aspecttable[8]);
+  for (i = 1; i < 7; i++) {
+    printf("  %6.6g - %-6.6g    :  %8d    | %6.6g - %-6.6g     :  %8d\n",
+           ratiotable[i - 1], ratiotable[i], aspecttable[i],
+           ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]);
+  }
+  printf("  %6.6g - %-6.6g    :  %8d    | %6.6g -            :  %8d\n",
+         ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14],
+         aspecttable[15]);
+  printf("  (Aspect ratio is longest edge divided by shortest altitude)\n\n");
+
+  printf("  Smallest angle: %15.5g   |  Largest angle: %15.5g\n\n",
+         smallestangle, biggestangle);
+
+  printf("  Angle histogram:\n");
+  for (i = 0; i < 9; i++) {
+    printf("    %3d - %3d degrees:  %8d    |    %3d - %3d degrees:  %8d\n",
+           i * 10, i * 10 + 10, angletable[i],
+           i * 10 + 90, i * 10 + 100, angletable[i + 9]);
+  }
+  printf("\n");
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  statistics()   Print all sorts of cool facts.                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void statistics(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void statistics(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  printf("\nStatistics:\n\n");
+  printf("  Input vertices: %d\n", m->invertices);
+  if (b->refine) {
+    printf("  Input triangles: %d\n", m->inelements);
+  }
+  if (b->poly) {
+    printf("  Input segments: %d\n", m->insegments);
+    if (!b->refine) {
+      printf("  Input holes: %d\n", m->holes);
+    }
+  }
+
+  printf("\n  Mesh vertices: %ld\n", m->vertices.items - m->undeads);
+  printf("  Mesh triangles: %ld\n", m->triangles.items);
+  printf("  Mesh edges: %ld\n", m->edges);
+  printf("  Mesh exterior boundary edges: %ld\n", m->hullsize);
+  if (b->poly || b->refine) {
+    printf("  Mesh interior boundary edges: %ld\n",
+           m->subsegs.items - m->hullsize);
+    printf("  Mesh subsegments (constrained edges): %ld\n",
+           m->subsegs.items);
+  }
+  printf("\n");
+
+  if (b->verbose) {
+    quality_statistics(m, b);
+    printf("Memory allocation statistics:\n\n");
+    printf("  Maximum number of vertices: %ld\n", m->vertices.maxitems);
+    printf("  Maximum number of triangles: %ld\n", m->triangles.maxitems);
+    if (m->subsegs.maxitems > 0) {
+      printf("  Maximum number of subsegments: %ld\n", m->subsegs.maxitems);
+    }
+    if (m->viri.maxitems > 0) {
+      printf("  Maximum number of viri: %ld\n", m->viri.maxitems);
+    }
+    if (m->badsubsegs.maxitems > 0) {
+      printf("  Maximum number of encroached subsegments: %ld\n",
+             m->badsubsegs.maxitems);
+    }
+    if (m->badtriangles.maxitems > 0) {
+      printf("  Maximum number of bad triangles: %ld\n",
+             m->badtriangles.maxitems);
+    }
+    if (m->flipstackers.maxitems > 0) {
+      printf("  Maximum number of stacked triangle flips: %ld\n",
+             m->flipstackers.maxitems);
+    }
+    if (m->splaynodes.maxitems > 0) {
+      printf("  Maximum number of splay tree nodes: %ld\n",
+             m->splaynodes.maxitems);
+    }
+    printf("  Approximate heap memory use (bytes): %ld\n\n",
+           m->vertices.maxitems * m->vertices.itembytes +
+           m->triangles.maxitems * m->triangles.itembytes +
+           m->subsegs.maxitems * m->subsegs.itembytes +
+           m->viri.maxitems * m->viri.itembytes +
+           m->badsubsegs.maxitems * m->badsubsegs.itembytes +
+           m->badtriangles.maxitems * m->badtriangles.itembytes +
+           m->flipstackers.maxitems * m->flipstackers.itembytes +
+           m->splaynodes.maxitems * m->splaynodes.itembytes);
+
+    printf("Algorithmic statistics:\n\n");
+    if (!b->weighted) {
+      printf("  Number of incircle tests: %ld\n", m->incirclecount);
+    } else {
+      printf("  Number of 3D orientation tests: %ld\n", m->orient3dcount);
+    }
+    printf("  Number of 2D orientation tests: %ld\n", m->counterclockcount);
+    if (m->hyperbolacount > 0) {
+      printf("  Number of right-of-hyperbola tests: %ld\n",
+             m->hyperbolacount);
+    }
+    if (m->circletopcount > 0) {
+      printf("  Number of circle top computations: %ld\n",
+             m->circletopcount);
+    }
+    if (m->circumcentercount > 0) {
+      printf("  Number of triangle circumcenter computations: %ld\n",
+             m->circumcentercount);
+    }
+    printf("\n");
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  main() or triangulate()   Gosh, do everything.                           */
+/*                                                                           */
+/*  The sequence is roughly as follows.  Many of these steps can be skipped, */
+/*  depending on the command line switches.                                  */
+/*                                                                           */
+/*  - Initialize constants and parse the command line.                       */
+/*  - Read the vertices from a file and either                               */
+/*    - triangulate them (no -r), or                                         */
+/*    - read an old mesh from files and reconstruct it (-r).                 */
+/*  - Insert the PSLG segments (-p), and possibly segments on the convex     */
+/*      hull (-c).                                                           */
+/*  - Read the holes (-p), regional attributes (-pA), and regional area      */
+/*      constraints (-pa).  Carve the holes and concavities, and spread the  */
+/*      regional attributes and area constraints.                            */
+/*  - Enforce the constraints on minimum angle (-q) and maximum area (-a).   */
+/*      Also enforce the conforming Delaunay property (-q and -a).           */
+/*  - Compute the number of edges in the resulting mesh.                     */
+/*  - Promote the mesh's linear triangles to higher order elements (-o).     */
+/*  - Write the output files and print the statistics.                       */
+/*  - Check the consistency and Delaunay property of the mesh (-C).          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void triangulate(char *triswitches, struct triangulateio *in,
+                 struct triangulateio *out, struct triangulateio *vorout)
+#else /* not ANSI_DECLARATORS */
+void triangulate(triswitches, in, out, vorout)
+char *triswitches;
+struct triangulateio *in;
+struct triangulateio *out;
+struct triangulateio *vorout;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+int main(int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+int main(argc, argv)
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+  struct mesh m;
+  struct behavior b;
+  REAL *holearray;                                        /* Array of holes. */
+  REAL *regionarray;   /* Array of regional attributes and area constraints. */
+#ifndef TRILIBRARY
+  FILE *polyfile;
+#endif /* not TRILIBRARY */
+#ifndef NO_TIMER
+  /* Variables for timing the performance of Triangle.  The types are */
+  /*   defined in sys/time.h.                                         */
+  struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6;
+  struct timezone tz;
+#endif /* not NO_TIMER */
+
+#ifndef NO_TIMER
+  gettimeofday(&tv0, &tz);
+#endif /* not NO_TIMER */
+
+  triangleinit(&m);
+#ifdef TRILIBRARY
+  parsecommandline(1, &triswitches, &b);
+#else /* not TRILIBRARY */
+  parsecommandline(argc, argv, &b);
+#endif /* not TRILIBRARY */
+  m.steinerleft = b.steiner;
+
+#ifdef TRILIBRARY
+  transfernodes(&m, &b, in->pointlist, in->pointattributelist,
+                in->pointmarkerlist, in->numberofpoints,
+                in->numberofpointattributes);
+#else /* not TRILIBRARY */
+  readnodes(&m, &b, b.innodefilename, b.inpolyfilename, &polyfile);
+#endif /* not TRILIBRARY */
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv1, &tz);
+  }
+#endif /* not NO_TIMER */
+
+#ifdef CDT_ONLY
+  m.hullsize = delaunay(&m, &b);                /* Triangulate the vertices. */
+#else /* not CDT_ONLY */
+  if (b.refine) {
+    /* Read and reconstruct a mesh. */
+#ifdef TRILIBRARY
+    m.hullsize = reconstruct(&m, &b, in->trianglelist,
+                             in->triangleattributelist, in->trianglearealist,
+                             in->numberoftriangles, in->numberofcorners,
+                             in->numberoftriangleattributes,
+                             in->segmentlist, in->segmentmarkerlist,
+                             in->numberofsegments);
+#else /* not TRILIBRARY */
+    m.hullsize = reconstruct(&m, &b, b.inelefilename, b.areafilename,
+                             b.inpolyfilename, polyfile);
+#endif /* not TRILIBRARY */
+  } else {
+    m.hullsize = delaunay(&m, &b);              /* Triangulate the vertices. */
+  }
+#endif /* not CDT_ONLY */
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv2, &tz);
+    if (b.refine) {
+      printf("Mesh reconstruction");
+    } else {
+      printf("Delaunay");
+    }
+    printf(" milliseconds:  %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) +
+           (tv2.tv_usec - tv1.tv_usec) / 1000l);
+  }
+#endif /* not NO_TIMER */
+
+  /* Ensure that no vertex can be mistaken for a triangular bounding */
+  /*   box vertex in insertvertex().                                 */
+  m.infvertex1 = (vertex) NULL;
+  m.infvertex2 = (vertex) NULL;
+  m.infvertex3 = (vertex) NULL;
+
+  if (b.usesegments) {
+    m.checksegments = 1;                /* Segments will be introduced next. */
+    if (!b.refine) {
+      /* Insert PSLG segments and/or convex hull segments. */
+#ifdef TRILIBRARY
+      formskeleton(&m, &b, in->segmentlist,
+                   in->segmentmarkerlist, in->numberofsegments);
+#else /* not TRILIBRARY */
+      formskeleton(&m, &b, polyfile, b.inpolyfilename);
+#endif /* not TRILIBRARY */
+    }
+  }
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv3, &tz);
+    if (b.usesegments && !b.refine) {
+      printf("Segment milliseconds:  %ld\n",
+             1000l * (tv3.tv_sec - tv2.tv_sec) +
+             (tv3.tv_usec - tv2.tv_usec) / 1000l);
+    }
+  }
+#endif /* not NO_TIMER */
+
+  if (b.poly && (m.triangles.items > 0)) {
+#ifdef TRILIBRARY
+    holearray = in->holelist;
+    m.holes = in->numberofholes;
+    regionarray = in->regionlist;
+    m.regions = in->numberofregions;
+#else /* not TRILIBRARY */
+    readholes(&m, &b, polyfile, b.inpolyfilename, &holearray, &m.holes,
+              &regionarray, &m.regions);
+#endif /* not TRILIBRARY */
+    if (!b.refine) {
+      /* Carve out holes and concavities. */
+      carveholes(&m, &b, holearray, m.holes, regionarray, m.regions);
+    }
+  } else {
+    /* Without a PSLG, there can be no holes or regional attributes   */
+    /*   or area constraints.  The following are set to zero to avoid */
+    /*   an accidental free() later.                                  */
+    m.holes = 0;
+    m.regions = 0;
+  }
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv4, &tz);
+    if (b.poly && !b.refine) {
+      printf("Hole milliseconds:  %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) +
+             (tv4.tv_usec - tv3.tv_usec) / 1000l);
+    }
+  }
+#endif /* not NO_TIMER */
+
+#ifndef CDT_ONLY
+  if (b.quality && (m.triangles.items > 0)) {
+    enforcequality(&m, &b);           /* Enforce angle and area constraints. */
+  }
+#endif /* not CDT_ONLY */
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv5, &tz);
+#ifndef CDT_ONLY
+    if (b.quality) {
+      printf("Quality milliseconds:  %ld\n",
+             1000l * (tv5.tv_sec - tv4.tv_sec) +
+             (tv5.tv_usec - tv4.tv_usec) / 1000l);
+    }
+#endif /* not CDT_ONLY */
+  }
+#endif /* not NO_TIMER */
+
+  /* Calculate the number of edges. */
+  m.edges = (3l * m.triangles.items + m.hullsize) / 2l;
+
+  if (b.order > 1) {
+    highorder(&m, &b);       /* Promote elements to higher polynomial order. */
+  }
+  if (!b.quiet) {
+    printf("\n");
+  }
+
+#ifdef TRILIBRARY
+  out->numberofpoints = m.vertices.items;
+  out->numberofpointattributes = m.nextras;
+  out->numberoftriangles = m.triangles.items;
+  out->numberofcorners = (b.order + 1) * (b.order + 2) / 2;
+  out->numberoftriangleattributes = m.eextras;
+  out->numberofedges = m.edges;
+  if (b.usesegments) {
+    out->numberofsegments = m.subsegs.items;
+  } else {
+    out->numberofsegments = m.hullsize;
+  }
+  if (vorout != (struct triangulateio *) NULL) {
+    vorout->numberofpoints = m.triangles.items;
+    vorout->numberofpointattributes = m.nextras;
+    vorout->numberofedges = m.edges;
+  }
+#endif /* TRILIBRARY */
+  /* If not using iteration numbers, don't write a .node file if one was */
+  /*   read, because the original one would be overwritten!              */
+  if (b.nonodewritten || (b.noiterationnum && m.readnodefile)) {
+    if (!b.quiet) {
+#ifdef TRILIBRARY
+      printf("NOT writing vertices.\n");
+#else /* not TRILIBRARY */
+      printf("NOT writing a .node file.\n");
+#endif /* not TRILIBRARY */
+    }
+    numbernodes(&m, &b);         /* We must remember to number the vertices. */
+  } else {
+    /* writenodes() numbers the vertices too. */
+#ifdef TRILIBRARY
+    writenodes(&m, &b, &out->pointlist, &out->pointattributelist,
+               &out->pointmarkerlist);
+#else /* not TRILIBRARY */
+    writenodes(&m, &b, b.outnodefilename, argc, argv);
+#endif /* TRILIBRARY */
+  }
+  if (b.noelewritten) {
+    if (!b.quiet) {
+#ifdef TRILIBRARY
+      printf("NOT writing triangles.\n");
+#else /* not TRILIBRARY */
+      printf("NOT writing an .ele file.\n");
+#endif /* not TRILIBRARY */
+    }
+  } else {
+#ifdef TRILIBRARY
+    writeelements(&m, &b, &out->trianglelist, &out->triangleattributelist);
+#else /* not TRILIBRARY */
+    writeelements(&m, &b, b.outelefilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+  /* The -c switch (convex switch) causes a PSLG to be written */
+  /*   even if none was read.                                  */
+  if (b.poly || b.convex) {
+    /* If not using iteration numbers, don't overwrite the .poly file. */
+    if (b.nopolywritten || b.noiterationnum) {
+      if (!b.quiet) {
+#ifdef TRILIBRARY
+        printf("NOT writing segments.\n");
+#else /* not TRILIBRARY */
+        printf("NOT writing a .poly file.\n");
+#endif /* not TRILIBRARY */
+      }
+    } else {
+#ifdef TRILIBRARY
+      writepoly(&m, &b, &out->segmentlist, &out->segmentmarkerlist);
+      out->numberofholes = m.holes;
+      out->numberofregions = m.regions;
+      if (b.poly) {
+        out->holelist = in->holelist;
+        out->regionlist = in->regionlist;
+      } else {
+        out->holelist = (REAL *) NULL;
+        out->regionlist = (REAL *) NULL;
+      }
+#else /* not TRILIBRARY */
+      writepoly(&m, &b, b.outpolyfilename, holearray, m.holes, regionarray,
+                m.regions, argc, argv);
+#endif /* not TRILIBRARY */
+    }
+  }
+#ifndef TRILIBRARY
+#ifndef CDT_ONLY
+  if (m.regions > 0) {
+    trifree((VOID *) regionarray);
+  }
+#endif /* not CDT_ONLY */
+  if (m.holes > 0) {
+    trifree((VOID *) holearray);
+  }
+  if (b.geomview) {
+    writeoff(&m, &b, b.offfilename, argc, argv);
+  }
+#endif /* not TRILIBRARY */
+  if (b.edgesout) {
+#ifdef TRILIBRARY
+    writeedges(&m, &b, &out->edgelist, &out->edgemarkerlist);
+#else /* not TRILIBRARY */
+    writeedges(&m, &b, b.edgefilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+  if (b.voronoi) {
+#ifdef TRILIBRARY
+    writevoronoi(&m, &b, &vorout->pointlist, &vorout->pointattributelist,
+                 &vorout->pointmarkerlist, &vorout->edgelist,
+                 &vorout->edgemarkerlist, &vorout->normlist);
+#else /* not TRILIBRARY */
+    writevoronoi(&m, &b, b.vnodefilename, b.vedgefilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+  if (b.neighbors) {
+#ifdef TRILIBRARY
+    writeneighbors(&m, &b, &out->neighborlist);
+#else /* not TRILIBRARY */
+    writeneighbors(&m, &b, b.neighborfilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+
+  if (!b.quiet) {
+#ifndef NO_TIMER
+    gettimeofday(&tv6, &tz);
+    printf("\nOutput milliseconds:  %ld\n",
+           1000l * (tv6.tv_sec - tv5.tv_sec) +
+           (tv6.tv_usec - tv5.tv_usec) / 1000l);
+    printf("Total running milliseconds:  %ld\n",
+           1000l * (tv6.tv_sec - tv0.tv_sec) +
+           (tv6.tv_usec - tv0.tv_usec) / 1000l);
+#endif /* not NO_TIMER */
+
+    statistics(&m, &b);
+  }
+
+#ifndef REDUCED
+  if (b.docheck) {
+    checkmesh(&m, &b);
+    checkdelaunay(&m, &b);
+  }
+#endif /* not REDUCED */
+
+  triangledeinit(&m, &b);
+#ifndef TRILIBRARY
+  return 0;
+#endif /* not TRILIBRARY */
+}
Index: /inundation/pmesh/triangle/triangle.h
===================================================================
--- /inundation/pmesh/triangle/triangle.h	(revision 1719)
+++ /inundation/pmesh/triangle/triangle.h	(revision 1719)
@@ -0,0 +1,282 @@
+/*****************************************************************************/
+/*                                                                           */
+/*  (triangle.h)                                                             */
+/*                                                                           */
+/*  Include file for programs that call Triangle.                            */
+/*                                                                           */
+/*  Accompanies Triangle Versions 1.3 and 1.4                                */
+/*  July 19, 1996                                                            */
+/*                                                                           */
+/*  Copyright 1996                                                           */
+/*  Jonathan Richard Shewchuk                                                */
+/*  2360 Woolsey #H                                                          */
+/*  Berkeley, California  94705-1927                                         */
+/*  jrs@cs.berkeley.edu                                                      */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  How to call Triangle from another program                                */
+/*                                                                           */
+/*                                                                           */
+/*  If you haven't read Triangle's instructions (run "triangle -h" to read   */
+/*  them), you won't understand what follows.                                */
+/*                                                                           */
+/*  Triangle must be compiled into an object file (triangle.o) with the      */
+/*  TRILIBRARY symbol defined (preferably by using the -DTRILIBRARY compiler */
+/*  switch).  The makefile included with Triangle will do this for you if    */
+/*  you run "make trilibrary".  The resulting object file can be called via  */
+/*  the procedure triangulate().                                             */
+/*                                                                           */
+/*  If the size of the object file is important to you, you may wish to      */
+/*  generate a reduced version of triangle.o.  The REDUCED symbol gets rid   */
+/*  of all features that are primarily of research interest.  Specifically,  */
+/*  the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches.  */
+/*  The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond  */
+/*  constrained Delaunay triangulation.  Specifically, the -DCDT_ONLY switch */
+/*  eliminates Triangle's -r, -q, -a, -S, and -s switches.                   */
+/*                                                                           */
+/*  IMPORTANT:  These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be    */
+/*  made in the makefile or in triangle.c itself.  Putting these definitions */
+/*  in this file will not create the desired effect.                         */
+/*                                                                           */
+/*                                                                           */
+/*  The calling convention for triangulate() follows.                        */
+/*                                                                           */
+/*      void triangulate(triswitches, in, out, vorout)                       */
+/*      char *triswitches;                                                   */
+/*      struct triangulateio *in;                                            */
+/*      struct triangulateio *out;                                           */
+/*      struct triangulateio *vorout;                                        */
+/*                                                                           */
+/*  `triswitches' is a string containing the command line switches you wish  */
+/*  to invoke.  No initial dash is required.  Some suggestions:              */
+/*                                                                           */
+/*  - You'll probably find it convenient to use the `z' switch so that       */
+/*    points (and other items) are numbered from zero.  This simplifies      */
+/*    indexing, because the first item of any type always starts at index    */
+/*    [0] of the corresponding array, whether that item's number is zero or  */
+/*    one.                                                                   */
+/*  - You'll probably want to use the `Q' (quiet) switch in your final code, */
+/*    but you can take advantage of Triangle's printed output (including the */
+/*    `V' switch) while debugging.                                           */
+/*  - If you are not using the `q' or `a' switches, then the output points   */
+/*    will be identical to the input points, except possibly for the         */
+/*    boundary markers.  If you don't need the boundary markers, you should  */
+/*    use the `N' (no nodes output) switch to save memory.  (If you do need  */
+/*    boundary markers, but need to save memory, a good nasty trick is to    */
+/*    set out->pointlist equal to in->pointlist before calling triangulate(),*/
+/*    so that Triangle overwrites the input points with identical copies.)   */
+/*  - The `I' (no iteration numbers) and `g' (.off file output) switches     */
+/*    have no effect when Triangle is compiled with TRILIBRARY defined.      */
+/*                                                                           */
+/*  `in', `out', and `vorout' are descriptions of the input, the output,     */
+/*  and the Voronoi output.  If the `v' (Voronoi output) switch is not used, */
+/*  `vorout' may be NULL.  `in' and `out' may never be NULL.                 */
+/*                                                                           */
+/*  Certain fields of the input and output structures must be initialized,   */
+/*  as described below.                                                      */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  The `triangulateio' structure.                                           */
+/*                                                                           */
+/*  Used to pass data into and out of the triangulate() procedure.           */
+/*                                                                           */
+/*                                                                           */
+/*  Arrays are used to store points, triangles, markers, and so forth.  In   */
+/*  all cases, the first item in any array is stored starting at index [0].  */
+/*  However, that item is item number `1' unless the `z' switch is used, in  */
+/*  which case it is item number `0'.  Hence, you may find it easier to      */
+/*  index points (and triangles in the neighbor list) if you use the `z'     */
+/*  switch.  Unless, of course, you're calling Triangle from a Fortran       */
+/*  program.                                                                 */
+/*                                                                           */
+/*  Description of fields (except the `numberof' fields, which are obvious): */
+/*                                                                           */
+/*  `pointlist':  An array of point coordinates.  The first point's x        */
+/*    coordinate is at index [0] and its y coordinate at index [1], followed */
+/*    by the coordinates of the remaining points.  Each point occupies two   */
+/*    REALs.                                                                 */
+/*  `pointattributelist':  An array of point attributes.  Each point's       */
+/*    attributes occupy `numberofpointattributes' REALs.                     */
+/*  `pointmarkerlist':  An array of point markers; one int per point.        */
+/*                                                                           */
+/*  `trianglelist':  An array of triangle corners.  The first triangle's     */
+/*    first corner is at index [0], followed by its other two corners in     */
+/*    counterclockwise order, followed by any other nodes if the triangle    */
+/*    represents a nonlinear element.  Each triangle occupies                */
+/*    `numberofcorners' ints.                                                */
+/*  `triangleattributelist':  An array of triangle attributes.  Each         */
+/*    triangle's attributes occupy `numberoftriangleattributes' REALs.       */
+/*  `trianglearealist':  An array of triangle area constraints; one REAL per */
+/*    triangle.  Input only.                                                 */
+/*  `neighborlist':  An array of triangle neighbors; three ints per          */
+/*    triangle.  Output only.                                                */
+/*                                                                           */
+/*  `segmentlist':  An array of segment endpoints.  The first segment's      */
+/*    endpoints are at indices [0] and [1], followed by the remaining        */
+/*    segments.  Two ints per segment.                                       */
+/*  `segmentmarkerlist':  An array of segment markers; one int per segment.  */
+/*                                                                           */
+/*  `holelist':  An array of holes.  The first hole's x and y coordinates    */
+/*    are at indices [0] and [1], followed by the remaining holes.  Two      */
+/*    REALs per hole.  Input only, although the pointer is copied to the     */
+/*    output structure for your convenience.                                 */
+/*                                                                           */
+/*  `regionlist':  An array of regional attributes and area constraints.     */
+/*    The first constraint's x and y coordinates are at indices [0] and [1], */
+/*    followed by the regional attribute and index [2], followed by the      */
+/*    maximum area at index [3], followed by the remaining area constraints. */
+/*    Four REALs per area constraint.  Note that each regional attribute is  */
+/*    used only if you select the `A' switch, and each area constraint is    */
+/*    used only if you select the `a' switch (with no number following), but */
+/*    omitting one of these switches does not change the memory layout.      */
+/*    Input only, although the pointer is copied to the output structure for */
+/*    your convenience.                                                      */
+/*                                                                           */
+/*  `edgelist':  An array of edge endpoints.  The first edge's endpoints are */
+/*    at indices [0] and [1], followed by the remaining edges.  Two ints per */
+/*    edge.  Output only.                                                    */
+/*  `edgemarkerlist':  An array of edge markers; one int per edge.  Output   */
+/*    only.                                                                  */
+/*  `normlist':  An array of normal vectors, used for infinite rays in       */
+/*    Voronoi diagrams.  The first normal vector's x and y magnitudes are    */
+/*    at indices [0] and [1], followed by the remaining vectors.  For each   */
+/*    finite edge in a Voronoi diagram, the normal vector written is the     */
+/*    zero vector.  Two REALs per edge.  Output only.                        */
+/*                                                                           */
+/*                                                                           */
+/*  Any input fields that Triangle will examine must be initialized.         */
+/*  Furthermore, for each output array that Triangle will write to, you      */
+/*  must either provide space by setting the appropriate pointer to point    */
+/*  to the space you want the data written to, or you must initialize the    */
+/*  pointer to NULL, which tells Triangle to allocate space for the results. */
+/*  The latter option is preferable, because Triangle always knows exactly   */
+/*  how much space to allocate.  The former option is provided mainly for    */
+/*  people who need to call Triangle from Fortran code, though it also makes */
+/*  possible some nasty space-saving tricks, like writing the output to the  */
+/*  same arrays as the input.                                                */
+/*                                                                           */
+/*  Triangle will not free() any input or output arrays, including those it  */
+/*  allocates itself; that's up to you.                                      */
+/*                                                                           */
+/*  Here's a guide to help you decide which fields you must initialize       */
+/*  before you call triangulate().                                           */
+/*                                                                           */
+/*  `in':                                                                    */
+/*                                                                           */
+/*    - `pointlist' must always point to a list of points; `numberofpoints'  */
+/*      and `numberofpointattributes' must be properly set.                  */
+/*      `pointmarkerlist' must either be set to NULL (in which case all      */
+/*      markers default to zero), or must point to a list of markers.  If    */
+/*      `numberofpointattributes' is not zero, `pointattributelist' must     */
+/*      point to a list of point attributes.                                 */
+/*    - If the `r' switch is used, `trianglelist' must point to a list of    */
+/*      triangles, and `numberoftriangles', `numberofcorners', and           */
+/*      `numberoftriangleattributes' must be properly set.  If               */
+/*      `numberoftriangleattributes' is not zero, `triangleattributelist'    */
+/*      must point to a list of triangle attributes.  If the `a' switch is   */
+/*      used (with no number following), `trianglearealist' must point to a  */
+/*      list of triangle area constraints.  `neighborlist' may be ignored.   */
+/*    - If the `p' switch is used, `segmentlist' must point to a list of     */
+/*      segments, `numberofsegments' must be properly set, and               */
+/*      `segmentmarkerlist' must either be set to NULL (in which case all    */
+/*      markers default to zero), or must point to a list of markers.        */
+/*    - If the `p' switch is used without the `r' switch, then               */
+/*      `numberofholes' and `numberofregions' must be properly set.  If      */
+/*      `numberofholes' is not zero, `holelist' must point to a list of      */
+/*      holes.  If `numberofregions' is not zero, `regionlist' must point to */
+/*      a list of region constraints.                                        */
+/*    - If the `p' switch is used, `holelist', `numberofholes',              */
+/*      `regionlist', and `numberofregions' is copied to `out'.  (You can    */
+/*      nonetheless get away with not initializing them if the `r' switch is */
+/*      used.)                                                               */
+/*    - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */
+/*      ignored.                                                             */
+/*                                                                           */
+/*  `out':                                                                   */
+/*                                                                           */
+/*    - `pointlist' must be initialized (NULL or pointing to memory) unless  */
+/*      the `N' switch is used.  `pointmarkerlist' must be initialized       */
+/*      unless the `N' or `B' switch is used.  If `N' is not used and        */
+/*      `in->numberofpointattributes' is not zero, `pointattributelist' must */
+/*      be initialized.                                                      */
+/*    - `trianglelist' must be initialized unless the `E' switch is used.    */
+/*      `neighborlist' must be initialized if the `n' switch is used.  If    */
+/*      the `E' switch is not used and (`in->numberofelementattributes' is   */
+/*      not zero or the `A' switch is used), `elementattributelist' must be  */
+/*      initialized.  `trianglearealist' may be ignored.                     */
+/*    - `segmentlist' must be initialized if the `p' or `c' switch is used,  */
+/*      and the `P' switch is not used.  `segmentmarkerlist' must also be    */
+/*      initialized under these circumstances unless the `B' switch is used. */
+/*    - `edgelist' must be initialized if the `e' switch is used.            */
+/*      `edgemarkerlist' must be initialized if the `e' switch is used and   */
+/*      the `B' switch is not.                                               */
+/*    - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/
+/*                                                                           */
+/*  `vorout' (only needed if `v' switch is used):                            */
+/*                                                                           */
+/*    - `pointlist' must be initialized.  If `in->numberofpointattributes'   */
+/*      is not zero, `pointattributelist' must be initialized.               */
+/*      `pointmarkerlist' may be ignored.                                    */
+/*    - `edgelist' and `normlist' must both be initialized.                  */
+/*      `edgemarkerlist' may be ignored.                                     */
+/*    - Everything else may be ignored.                                      */
+/*                                                                           */
+/*  After a call to triangulate(), the valid fields of `out' and `vorout'    */
+/*  will depend, in an obvious way, on the choice of switches used.  Note    */
+/*  that when the `p' switch is used, the pointers `holelist' and            */
+/*  `regionlist' are copied from `in' to `out', but no new space is          */
+/*  allocated; be careful that you don't free() the same array twice.  On    */
+/*  the other hand, Triangle will never copy the `pointlist' pointer (or any */
+/*  others); new space is allocated for `out->pointlist', or if the `N'      */
+/*  switch is used, `out->pointlist' remains uninitialized.                  */
+/*                                                                           */
+/*  All of the meaningful `numberof' fields will be properly set; for        */
+/*  instance, `numberofedges' will represent the number of edges in the      */
+/*  triangulation whether or not the edges were written.  If segments are    */
+/*  not used, `numberofsegments' will indicate the number of boundary edges. */
+/*                                                                           */
+/*****************************************************************************/
+
+struct triangulateio {
+  REAL *pointlist;                                               /* In / out */
+  REAL *pointattributelist;                                      /* In / out */
+  int *pointmarkerlist;                                          /* In / out */
+  int numberofpoints;                                            /* In / out */
+  int numberofpointattributes;                                   /* In / out */
+
+  int *trianglelist;                                             /* In / out */
+  REAL *triangleattributelist;                                   /* In / out */
+  REAL *trianglearealist;                                         /* In only */
+  int *neighborlist;                                             /* Out only */
+  int numberoftriangles;                                         /* In / out */
+  int numberofcorners;                                           /* In / out */
+  int numberoftriangleattributes;                                /* In / out */
+
+  int *segmentlist;                                              /* In / out */
+  int *segmentmarkerlist;                                        /* In / out */
+  int numberofsegments;                                          /* In / out */
+
+  REAL *holelist;                        /* In / pointer to array copied out */
+  int numberofholes;                                      /* In / copied out */
+
+  REAL *regionlist;                      /* In / pointer to array copied out */
+  int numberofregions;                                    /* In / copied out */
+
+  int *edgelist;                                                 /* Out only */
+  int *edgemarkerlist;            /* Not used with Voronoi diagram; out only */
+  REAL *normlist;                /* Used only with Voronoi diagram; out only */
+  int numberofedges;                                             /* Out only */
+};
+
+#ifdef ANSI_DECLARATORS
+void triangulate(char *, struct triangulateio *, struct triangulateio *,
+                 struct triangulateio *);
+#else /* not ANSI_DECLARATORS */
+void triangulate();
+#endif /* not ANSI_DECLARATORS */
Index: /inundation/pmesh/triangle/triangmodule.c
===================================================================
--- /inundation/pmesh/triangle/triangmodule.c	(revision 1719)
+++ /inundation/pmesh/triangle/triangmodule.c	(revision 1719)
@@ -0,0 +1,573 @@
+/* this appears to be necessary to run on Linux */
+
+#undef _STDLIB_H_
+#define _STDLIB_H_
+
+
+/*
+    This code is based on code by A. Pletzer
+
+    This code interfaces pmesh directly with "triangle", a general        
+    purpose triangulation code. In doing so, Python data structures          
+    are passed to C for  use by "triangle" and the results are then          
+    converted back. This was accomplished using the Python/C API.            
+                                                                           
+    I. Lists of points,edges,holes, and regions must normally be 
+     created for proper triangulation. However, if there are no holes,
+     the  hole list need not be specified (however, a 
+     measure of control over the shape of the convex hull is lost; 
+     triangle decides). 
+     
+     points list: [(x1,y1),(x2,y2),...] (Tuples of doubles)                  
+     edge list: [(Ea,Eb),(Ef,Eg),...] (Tuples of integers)                   
+     hole list: [(x1,y1),...](Tuples of doubles, one inside each hole region)
+     regionlist: [ (x1,y1,index,area),...] (Tuple of doubles)                
+     pointattributelist:[ (a1,a2..),...] (Tuple of lists) 
+     mode: String - the commands sent to triangle
+          
+     This is all that is needed to accomplish an intial triangulation.       
+     A dictionary of the Triangle output is returned.                                   
+     Precondition
+     End list in the pointattributelist has to have the same length
+     
+    Compilation can be done as follows                                       
+                    
+    c++ -I/usr/local/include/python1.5 -shared -o triangmodule.so            
+    triangmodule.c triangle.o                                                */
+
+
+#ifdef SINGLE  
+#define REAL float 
+#else  
+#define REAL double 
+#endif  
+
+#include "triangle.h" 
+
+#ifndef _STDLIB_H_ 
+#ifndef _WIN32
+extern "C" void *malloc(); 
+extern "C" void free(); 
+#endif
+#endif  
+
+#include "Python.h" 
+
+
+ static PyObject *triang_genMesh(PyObject *self, PyObject *args){
+     
+  /* Mesh generation routine
+     pre-conditions:
+     mode must have a 'z' switch, to avoid segmentation faults
+  */
+    
+  struct triangulateio in,out;
+
+  PyObject *hull;
+  PyObject *seglist;
+  PyObject *holelist;
+  PyObject *regionlist;
+  PyObject *trianglelist;
+  PyObject *mode;
+  PyObject *firstPointAttr;
+  PyObject *pointattributelist;
+  PyObject *segmarkerlist;
+  PyObject *Attrlist;
+  REAL Attr;
+  int i, j, iatt, n;
+  int a,b,c;
+  int marker;
+  int tuplesize;
+    
+  int index = 0;
+  double x,y;
+  PyObject *elems;
+  PyObject *pair;
+     
+  char *mod;
+  PyObject *holder, *holderlist, *attributelist;
+  int listsize,attsize ;
+  PyObject  *ii;
+     
+  if(!PyArg_ParseTuple(args,(char *)"OOOOOOOO",&hull,&seglist,&holelist,&regionlist,&pointattributelist,&segmarkerlist,&trianglelist,&mode)){
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError, 
+		    "incorrect first argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect second argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect third argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect fourth argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect fifth argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect sixth argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect seventh argument for 'genMesh': list required.");
+    return NULL;
+  }
+  if(!PyList_Check(hull)){
+    PyErr_SetString(PyExc_TypeError,
+		    "incorrect eighth argument for 'genMesh': list required.");
+    return NULL;
+  }
+     
+     
+  /* Initialize  points */
+  in.numberofpoints =  PyList_Size(hull); 
+  in.pointlist = (REAL *)malloc(in.numberofpoints*2*sizeof(REAL));
+  in.pointmarkerlist = (int *)NULL; /* malloc(in.numberofpoints*sizeof(int)); */
+     
+  /* Initialize and fill up region list */
+  in.numberofregions = PyList_Size(regionlist);
+  
+  /*printf(" numberofregions %i\n",in.numberofregions );*/
+  in.regionlist = (REAL *)malloc(in.numberofregions*4*sizeof(REAL));
+  for(i=0; i<in.numberofregions;i++){
+    elems = PyList_GetItem(regionlist,i);
+    tuplesize = PySequence_Length(elems);
+    /*printf("tuplesize %i\n",tuplesize);*/
+    if (tuplesize>0){
+      in.regionlist[4*i] = PyFloat_AsDouble(PyTuple_GetItem(elems,0));
+    } else {
+      PyErr_SetString(PyExc_TypeError,
+		      "data sent to trianglemodule error: A region has no x value.");
+      return NULL;
+    }
+    if (tuplesize>1){
+      in.regionlist[4*i+1] = PyFloat_AsDouble(PyTuple_GetItem(elems,1));
+    } else {
+      PyErr_SetString(PyExc_TypeError,
+		    "data sent to trianglemodule error: A region has no y value.");
+      return NULL;
+    }
+    if (tuplesize>2){
+      /* 2 is attribute*/
+      in.regionlist[4*i+2] = PyFloat_AsDouble(PyTuple_GetItem(elems,2));
+    } else {
+      PyErr_SetString(PyExc_TypeError,
+		    "data sent to trianglemodule error: A region has no attribute value.");
+      return NULL;
+    }
+    if (tuplesize>3){
+      in.regionlist[4*i+3] = PyFloat_AsDouble(PyTuple_GetItem(elems,3));
+    } else {
+      /* 0.0 is interpreted as no local max area by triangle  */
+      in.regionlist[4*i+3] = (REAL)0.0;
+    } 
+  }
+  
+  /*Initialize segments */
+  in.numberofsegments = PyList_Size(seglist);
+  in.segmentlist = (int *)malloc(in.numberofsegments*2*sizeof(int));
+  in.segmentmarkerlist = (int *)malloc(in.numberofsegments*sizeof(int));
+
+    
+  /*Initialize triangles */
+  in.numberofcorners = 3;
+  in.numberoftriangles = PyList_Size(trianglelist);
+  if (in.numberoftriangles  != 0) {
+    in.trianglelist = (int *)malloc(in.numberofsegments*3*sizeof(int));
+  } else {
+    in.trianglelist = (int *)NULL;
+  }
+  in.numberoftriangleattributes = 0;  
+  in.triangleattributelist  = (REAL *)NULL; 
+     
+     
+     
+  /*Initialize holes */
+  in.numberofholes = PyList_Size(holelist);
+  if (in.numberofholes != 0) {
+    in.holelist = (REAL *)malloc(in.numberofholes*2*sizeof(REAL));
+  } else {
+    in.holelist = (REAL *)NULL;
+  }     
+     
+  /* Fill up triangle list */
+  index = 0;
+  for(i = 0; i < in.numberoftriangles; i++) {
+    pair =  PyList_GetItem(trianglelist,i);
+    if (3 != PySequence_Length(pair)) {
+      PyErr_SetString(PyExc_TypeError,
+		      "data sent to trianglemodule error:Found a triangle without 3 vertices.");
+      return NULL;
+    }
+    a = (int)PyInt_AsLong(PyTuple_GetItem(pair,0));
+    in.trianglelist[index] = a;
+    index++;
+    b = (int)PyInt_AsLong(PyTuple_GetItem(pair,1));
+    in.trianglelist[index] = b;
+    index++;
+    c = (int)PyInt_AsLong(PyTuple_GetItem(pair,2));
+    in.trianglelist[index] = c;
+    index++;
+  }
+     
+     
+  /*  print the triangle list DEBUGGING
+      printf ("numberoftriangles %i\n", in.numberoftriangles);
+      for(i = 0; i < in.numberoftriangles; i++) {
+      printf("(%i,%i,%i)",in.trianglelist[i* 3+ 0],in.trianglelist[i* 3+ 1], in.trianglelist[i* 3+ 2]);
+      }*/
+     
+  /* Fill up segment list */
+  index = 0;
+  if (in.numberofsegments != PyList_Size(segmarkerlist)) {
+    PyErr_SetString(PyExc_TypeError,
+		    "data sent to trianglemodule error:number of segments doesn't = number of segment attributes.");
+    return NULL;
+  }
+    
+  for(i = 0; i < in.numberofsegments; i++) {
+    pair =  PyList_GetItem(seglist,i);
+    if (2 != PySequence_Length(pair)) {
+      PyErr_SetString(PyExc_TypeError,
+		      "data sent to trianglemodule error:Found a segment without 2 vertices.");
+      return NULL;
+    }
+    a = (int)PyInt_AsLong(PyTuple_GetItem(pair,0));
+    in.segmentlist[index] = a;
+    index++;
+    b = (int)PyInt_AsLong(PyTuple_GetItem(pair,1));
+    in.segmentlist[index] = b;
+    index++;
+    /* Fill up segment marker list */
+    marker =  (int)PyInt_AsLong(PyList_GetItem(segmarkerlist,i));
+    in.segmentmarkerlist[i] = marker;
+  }
+  /* Fill up hole list */
+  index = 0;
+  for(i = 0; i < in.numberofholes; i++) {
+    pair =  PyList_GetItem(holelist,i);
+    if (2 != PySequence_Length(pair)) {
+      PyErr_SetString(PyExc_TypeError,
+		      "data sent to trianglemodule error:Found a pair without 2 vertices.");
+      return NULL;
+    }
+    x = PyFloat_AsDouble(PyTuple_GetItem(pair,0));
+    in.holelist[index] = x;
+    index++;
+    y = PyFloat_AsDouble(PyTuple_GetItem(pair,1));
+    in.holelist[index] = y;
+    index++;
+  }
+     
+  /* Fill up point list */
+  index = 0;
+  for(i = 0; i < in.numberofpoints; i++) {
+    pair =  PyList_GetItem(hull,i);
+    if (2 != PySequence_Length(pair)) {
+      PyErr_SetString(PyExc_TypeError,
+		      "data sent to trianglemodule error:Found a vertex without an x,y values.");
+      return NULL;
+    }
+    x = PyFloat_AsDouble(PyTuple_GetItem(pair,0));
+    in.pointlist[index] = x;
+    index++;
+    y = PyFloat_AsDouble(PyTuple_GetItem(pair,1));
+    in.pointlist[index] = y;
+    index++;
+  }
+
+  /* Initialize  point attributes */
+/*      firstPointAttr = PyList_GetItem(pointattributelist,0); */
+/*      if (firstPointAttr != NULL) { */
+/*        //in.numberofpointattributes = PyList_Size(firstPointAttr); */
+/*        printf("in.numberofpointattributes =%i",PyList_Size(firstPointAttr)); */
+/*        //printf("in.numberofpointattributes =%i",in.numberofpointattributes); */
+/*      } else { */
+/*        printf("(firstPointAttr is NULL\n"); */
+/*        in.pointattributelist = NULL; */
+/*        in.numberofpointattributes = 0; */
+/*      } */
+  n = PyList_Size(pointattributelist);
+  if (n < 0) {
+    in.numberofpointattributes = 0; 
+    in.pointattributelist = (REAL *)NULL;
+  } else {
+    /* Get the # of attributes based on the size of the first point*/
+    firstPointAttr = PyList_GetItem(pointattributelist,0);
+    if (firstPointAttr == NULL) {
+      in.numberofpointattributes = 0; 
+      in.pointattributelist = (REAL *)NULL;
+    } else {
+      in.numberofpointattributes = PyList_Size(firstPointAttr);
+      in.pointattributelist = (REAL *)malloc(in.numberofpoints
+					     *in.numberofpointattributes
+					     *sizeof(REAL)); 
+     
+     
+      /* fill the point attribute list */
+      if (in.numberofpointattributes != 0) {
+	for(j = 0; j < in.numberofpoints; j++){
+	  Attrlist = PyList_GetItem(pointattributelist,j);
+	  if (in.numberofpointattributes != PySequence_Length(Attrlist)) {
+	    PyErr_SetString(PyExc_TypeError,
+			    "data sent to trianglemodule error:List size of attributes is different from vertex to vertex.");
+	    return NULL;
+	  }
+	  for(i = 0; i < in.numberofpointattributes; i++){
+	    Attr = PyFloat_AsDouble(PyList_GetItem(Attrlist,i));
+	    in.pointattributelist[in.numberofpointattributes*j+i]= (REAL)Attr;
+	    /* 	 printf("i =%i\n",i);  */
+	    /* 	 printf("j =%i\n",j);  */
+	    /* 	 printf("in.pointattributelist[4*j+i]
+		 =%f\n",in.pointattributelist[4*j+i]); */
+	  }
+	}
+      } 
+    }
+  }
+    
+     
+  /* this half from triangulate*/
+  /* set up the switch arguments to the triangulation routine */
+
+  mod = PyString_AS_STRING(mode);
+     
+         
+  out.pointlist = (REAL *)NULL;
+  out.pointmarkerlist = (int *)NULL;
+  out.numberofpointattributes = in.numberofpointattributes;
+  out.pointattributelist = (REAL *)NULL;
+  
+  out.trianglelist = (int *)NULL;
+  out.triangleattributelist = (REAL *)NULL;                   
+  out.trianglearealist = (REAL *)NULL;                        
+  out.neighborlist = (int *)NULL;
+     
+  out.segmentlist = (int *)NULL;
+  out.segmentmarkerlist = (int *)NULL;
+     
+  out.edgelist = (int *)NULL;
+  out.edgemarkerlist = (int *)NULL;
+     
+  out.holelist = (REAL *)NULL;
+  out.regionlist = (REAL *)NULL;
+    
+  /*printf("\n\nTriangulate input args: %s \n\n", mod); */
+  triangulate(mod, &in, &out, (struct triangulateio *)NULL );
+
+  /*
+    ------- Pass point numbers,coordinates and neighbors back to Python ------
+    we send back a dictionary:                                               
+    { index : [ coordinates, [connections], Attribute ] } 
+  */
+  holder = PyDict_New();
+     
+  /* Add pointlist */
+  listsize = out.numberofpoints;
+  holderlist = PyList_New(listsize);
+     
+  for(i=0; i<listsize;i++){
+    PyObject *mlist = Py_BuildValue((char *)"(d,d)", 
+				    out.pointlist[i*2  ], out.pointlist[i*2+1]);  
+    PyList_SetItem(holderlist,i, mlist);
+  }  
+  ii=PyString_FromString("generatedpointlist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);
+    
+  /* Add point marker list */
+  listsize = out.numberofpoints;
+  holderlist = PyList_New(listsize);
+  
+  for(i=0; i<listsize;i++){
+    PyObject *mlist = Py_BuildValue((char *)"d", 
+				    out.pointmarkerlist[i]);     
+    PyList_SetItem(holderlist,i, mlist); 
+  }  
+  ii=PyString_FromString("generatedpointmarkerlist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);
+    
+  /* Add point attribute list */
+  listsize = out.numberofpoints;
+  holderlist = PyList_New(listsize);
+  
+  for(i=0; i<listsize;i++){
+    attsize = out.numberofpointattributes;
+    attributelist = PyList_New(attsize);
+    for(iatt=0; iatt<attsize;iatt++){
+      PyObject *mlist = Py_BuildValue((char *)"d", 
+				      out.pointattributelist[i*attsize + iatt]);     
+      PyList_SetItem(attributelist,iatt, mlist);
+    }      
+    PyList_SetItem(holderlist,i, attributelist);
+  }  
+  ii=PyString_FromString("generatedpointattributelist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);  
+  
+  /* Add triangle list */
+  listsize = out.numberoftriangles;
+  holderlist = PyList_New(listsize);
+  for(i=0; i<listsize;i++){
+    PyObject *mlist = Py_BuildValue((char *)"(i,i,i)", 
+				    out.trianglelist[i*3  ], out.trianglelist [i*3+1], out.trianglelist [i*3+2]);    
+    PyList_SetItem(holderlist,i, mlist);
+  }    
+  ii=PyString_FromString("generatedtrianglelist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);
+     
+     
+  /* Add triangle attribute list */
+  listsize = out.numberoftriangles;
+  holderlist = PyList_New(listsize);
+     
+  for(i=0; i<listsize; i++){
+    attsize = out.numberoftriangleattributes;
+    attributelist = PyList_New(attsize);       
+    for(iatt=0; iatt<attsize;iatt++){
+      PyObject *mlist = Py_BuildValue((char *)"d",out.triangleattributelist[i*attsize + iatt]);     
+      PyList_SetItem(attributelist,iatt, mlist);
+    }      
+    PyList_SetItem(holderlist,i, attributelist);
+  }  
+  ii=PyString_FromString("generatedtriangleattributelist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);
+  
+  /* Add segment list */
+  listsize = out.numberofsegments;
+  holderlist = PyList_New(listsize);
+  for(i=0; i<listsize;i++){
+    PyObject *mlist = Py_BuildValue((char *)"(i,i)", 
+				    out.segmentlist[i*2  ],
+				    out.segmentlist [i*2+1]);
+    PyList_SetItem(holderlist,i, mlist);
+  }    
+  ii=PyString_FromString("generatedsegmentlist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);  
+  
+  /* Add segment marker list */
+  listsize = out.numberofsegments;
+  holderlist = PyList_New(listsize);
+  for(i=0; i<listsize;i++){
+    PyObject *mlist = Py_BuildValue((char *)"i", 
+				    out.segmentmarkerlist[i]);
+    PyList_SetItem(holderlist,i, mlist);
+  }    
+  ii=PyString_FromString("generatedsegmentmarkerlist");
+  PyDict_SetItem(holder, ii, holderlist); Py_DECREF(ii); Py_DECREF(holderlist);  
+  /* Add triangle neighbor list */
+  if (out.neighborlist != NULL) {
+    listsize = out.numberoftriangles;
+    holderlist = PyList_New(listsize);
+    for(i=0; i<listsize;i++){
+      PyObject *mlist = Py_BuildValue((char *)"(i,i,i)", 
+				      out.neighborlist[i*3  ],
+				      out.neighborlist [i*3+1],
+				      out.neighborlist [i*3+2]);    
+      PyList_SetItem(holderlist,i, mlist);
+    }    
+    ii=PyString_FromString("generatedtriangleneighborlist");
+    PyDict_SetItem(holder, ii, holderlist);
+    Py_DECREF(ii); Py_DECREF(holderlist);
+  }   
+     
+  /* Free in/out structure memory */
+  
+  /* OUT */
+
+  if(!out.pointlist){
+    free(out.pointlist);  out.pointlist=NULL;
+  }
+  if(!out.pointmarkerlist){    
+    free(out.pointmarkerlist); out.pointmarkerlist=NULL;
+  }
+  if(!out.pointattributelist){    
+    free(out.pointattributelist); out.pointattributelist=NULL;
+  }
+    
+ 
+  if(!out.trianglelist){    
+    free(out.trianglelist); out.trianglelist=NULL;
+  }
+  if(!out.triangleattributelist){    
+    free(out.triangleattributelist); out.triangleattributelist=NULL;
+  }
+  if(!out.trianglearealist){    
+    free(out.trianglearealist); out.trianglearealist=NULL;
+  }
+  if(!out.neighborlist){    
+    free(out.neighborlist); out.neighborlist=NULL;
+  }
+  if(!out.segmentlist){
+    free(out.segmentlist); out.segmentlist =NULL;
+  }
+  if(!out.segmentmarkerlist){
+    free(out.segmentmarkerlist); out.segmentmarkerlist  =NULL;
+  }
+  if(!out.edgelist){
+    free(out.edgelist);  out.edgelist=NULL;
+  }
+  if(!out.edgemarkerlist){
+    free(out.edgemarkerlist);  out.edgemarkerlist=NULL;
+  }
+  if(!out.holelist){
+    free(out.holelist); out.holelist=NULL;
+  }
+  if(!out.regionlist){
+    free(out.regionlist); out.regionlist=NULL;
+  }
+    
+  /*  IN */
+  
+  if(!in.pointlist){
+    free(in.pointlist);  in.pointlist   =NULL;             
+  }
+  if(!in.pointattributelist){
+    free(in.pointattributelist); in.pointattributelist =NULL;       
+  }
+  if(!in.pointmarkerlist){
+    free(in.pointmarkerlist); in.pointmarkerlist    =NULL;        
+  }
+  if(!in.segmentlist){
+    free(in.segmentlist);  in.segmentlist    =NULL;         
+  }
+  if(!in.segmentmarkerlist){
+    free(in.segmentmarkerlist); in.segmentmarkerlist    =NULL;   
+  }
+  if(!in.regionlist){
+    free(in.regionlist); in.regionlist  =NULL;            
+  }
+  if(!in.holelist){
+    free(in.holelist); in.holelist=NULL;
+  }
+
+  //return Py_BuildValue((char *)"O", holder);
+  return Py_BuildValue((char *)"O", holder);
+}
+
+/* end of my function*/
+
+
+   
+    
+static PyMethodDef triang_methods[] = {
+  {(char *)"genMesh", triang_genMesh, 1, (char *)"Passes hull points to triangle.All arguments are lists.(<hull>,<seglist>,<holelist>,<regionlist>)-><hull>"}, 
+  {NULL,NULL}
+};
+
+void inittriang(){
+  Py_InitModule((char *)"triang",triang_methods);
+}    
Index: /inundation/pmesh/triangmoduleHarness.py
===================================================================
--- /inundation/pmesh/triangmoduleHarness.py	(revision 1719)
+++ /inundation/pmesh/triangmoduleHarness.py	(revision 1719)
@@ -0,0 +1,286 @@
+#!/usr/bin/env python
+#
+
+import unittest
+import triang
+
+class triangTestCase(unittest.TestCase):
+    def setUp(self):
+        pass
+        
+    def tearDown(self):
+        pass
+ 
+    def testrectangle(self):
+
+        points = []
+        seglist = []
+        segattlist = []
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	# This will cause a seg fault
+        #points = [[0.0,0.0],[0.0,10.0],[3.0,0.0],[3.0,10.0]]
+
+        # automatically generate pointattlist
+	pointattlist = []
+        for point in points:
+            pointattlist.append([])
+        #print "pointattlist",pointattlist
+	#pointattlist = [[],[],[],[]]
+        trilist = []
+        mode = "Qzcn"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist,trilist, mode)
+        self.failUnless(data['generatedtrianglelist'] ==[(1, 0, 2), (2, 3, 1)],
+                        'generatedtrianglelist is wrong!')
+        #print "data['generatedsegmentlist']",data['generatedsegmentlist']
+        self.failUnless(data['generatedsegmentlist'] ==[(2, 0), (3, 2), (1, 3),
+                                                        (0, 1)]
+              ,          'generatedsegmentlist is wrong!')
+        self.failUnless(data['generatedpointlist'] ==[(0.0, 0.0), (0.0, 10.0),
+                                                      (3.0, 0.0), (3.0, 10.0)],
+                        ' is wrong!')
+        
+        #print "data['generatedtriangleneighborlist']",
+        #data['generatedtriangleneighborlist'] 
+        self.failUnless(data['generatedtriangleneighborlist'] ==[(-1, 1, -1),
+                                                                 (-1, 0, -1)],
+                        'generatedtriangleneighborlist is wrong!')
+
+        
+    def testrectangleII(self):
+
+        points = []
+        seglist = []
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+	regionlist.append( (1.2,1.2,5.0) )
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [0,0,0,0]
+        trilist = []
+        mode = "Qzp"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist,trilist, mode)
+        
+        self.failUnless(data['generatedtrianglelist'] ==[(1, 0, 2), (2, 3, 1)],
+                        'trianglelist is wrong!')
+        self.failUnless(data['generatedsegmentlist'] ==[(0, 1), (1, 3),
+                                                        (3, 2), (2, 0)],
+                        'segmentlist is wrong!')
+        self.failUnless(data['generatedpointlist'] ==[(0.0, 0.0), (0.0, 10.0),
+                                                      (3.0, 0.0), (3.0, 10.0)],
+                        ' is wrong!')
+   
+    def testsegmarker(self):
+
+        points = []
+        seglist = []
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+	regionlist.append( (1.2,1.2,5.0) )
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [1.0,2.0,3.0,4.0]
+        trilist = []
+        mode = "Qzp"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist,trilist, mode)
+        
+        self.failUnless(data['generatedtrianglelist'] ==[(1, 0, 2), (2, 3, 1)],
+                        'trianglelist is wrong!')
+        self.failUnless(data['generatedsegmentlist'] ==[(0, 1), (1, 3),
+                                                        (3, 2), (2, 0)],
+                        'segmentlist is wrong!')
+        self.failUnless(data['generatedpointlist'] ==[(0.0, 0.0), (0.0, 10.0),
+                                                      (3.0, 0.0), (3.0, 10.0)],
+                        ' is wrong!')
+        self.failUnless(data['generatedsegmentmarkerlist'] ==[1,2,3,4],
+                        ' is wrong!')
+             
+    def testbad_region(self):
+
+        points = []
+        seglist = []
+        holelist = []
+        regionlist = [(1.2,1.2)]
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [0,0,0,0]
+        trilist = []
+        mode = "Qzpn"
+        try:
+            data = triang.genMesh(points,seglist,holelist,
+                                  regionlist,pointattlist,segattlist,
+                                  trilist, mode)
+            
+        except TypeError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad region list did not raise error!')
+      
+    def testregion_with_maxarea(self):
+
+        points = []
+        seglist = []
+        holelist = []
+        regionlist = [(3,1,1.0)]
+
+	points = [(0.0,0.0),(6.0,0.0),(6.0,6.0),(0.0,6.0)]
+	pointattlist = [[],[],[],[]]
+	seglist = [(0,1),(1,2),(3,2),(3,0),(0,2)]
+        segattlist = [0,0,0,0,0]
+        trilist = []
+        mode = "Qzpna36a"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist, trilist, mode)
+        self.failUnless(len(data['generatedtrianglelist']) == 2,
+                        'testregion_with_maxarea 1: # of tris is wrong!')
+        ## Another test case    
+        regionlist = [(3,1,1.0),(1,3,1.0,8.0)]
+        mode = "Qzpna36a"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist, trilist, mode)
+        self.failUnless(len(data['generatedtrianglelist']) == 6,
+                        'testregion_with_maxarea 2: # of tris is wrong!')
+        ## Another test case    
+        regionlist = [(3,1,1.0,8.0),(1,3,1.0,8.0)]
+        mode = "Qzpna36a"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist, trilist, mode)
+        self.failUnless(len(data['generatedtrianglelist']) == 8,
+                        'testregion_with_maxarea 3: # of tris is wrong!')
+                  
+        ## Another test case    
+        regionlist = [(3,1,1.0),(1,3,1.0,8.0)]
+        mode = "Qzpna8a"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist, trilist, mode)
+        self.failUnless(len(data['generatedtrianglelist']) == 8,
+                        'testregion_with_maxarea 4: # of tris is wrong!')
+                  
+                  
+    def testbad_point(self):
+
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0)]
+	pointattlist = [[],[],[],[]]
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [0,0,0,0]
+        trilist = []
+        mode = "Qzpn"
+        try:
+            data = triang.genMesh(points,seglist,holelist,regionlist,
+                                  pointattlist,segattlist, trilist, mode)
+            
+        except TypeError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad point list did not raise error!')
+      
+    def testbad_hole(self):
+
+        holelist = [(9.0)]
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [0,0,0,0]
+        trilist = []
+        mode = "Qzpn"
+        try:
+            data = triang.genMesh(points,seglist,holelist,regionlist,
+                                  pointattlist,segattlist, trilist, mode)
+            
+        except TypeError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad hole list did not raise error!')
+      
+    def testbad_segment(self):
+
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+	seglist = [(0,1),(1,3),(3,2),(2)]
+        segattlist = [0,0,0,0]
+        trilist = []
+        mode = "Qzpn"
+        try:
+            data = triang.genMesh(points,seglist,holelist,regionlist,
+                                  pointattlist,segattlist, trilist, mode)
+            
+        except TypeError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad segment list did not raise error!')
+     
+    def testbad_segattlist(self):
+
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [0,0]
+        trilist = []
+        mode = "Qzpn"
+        try:
+            data = triang.genMesh(points,seglist,holelist,regionlist,
+                                  pointattlist,segattlist, trilist, mode)
+            
+            self.failUnless(data['trianglelist'] ==[(1, 0, 2), (2, 3, 1)],
+                        'trianglelist is wrong!')
+        except TypeError:
+            pass
+        else:
+            self.failUnless(0 ==1,
+                        'bad segment attribute list did not raise error!')
+            
+    def testrectangle_regions(self):
+
+        points = []
+        seglist = []
+        holelist = []
+        regionlist = []
+
+	points = [(0.0,0.0),(0.0,10.0),(3.0,0.0),(3.0,10.0)]
+	pointattlist = [[],[],[],[]]
+        #it seems that
+        #triangle only associates one region with a triangle
+	regionlist.append( (1.3,1.3,88.33) )
+	regionlist.append( (1.2,1.2,77,55) )
+	seglist = [(0,1),(1,3),(3,2),(2,0)]
+        segattlist = [0,0,0,0]
+        trilist = []
+        mode = "QAzpq"
+        data = triang.genMesh(points,seglist,holelist,regionlist,
+                              pointattlist,segattlist, trilist, mode)
+           
+        self.failUnless(data['generatedtriangleattributelist'] ==[[77.0], [77.0], [77.0], [77.0]],
+                        'triangleattributelist is wrong!')
+        
+        
+if __name__ == "__main__":
+
+    suite = unittest.makeSuite(triangTestCase,'test')
+    runner = unittest.TextTestRunner()
+    runner.run(suite)
Index: /inundation/pmesh/util_ext.c
===================================================================
--- /inundation/pmesh/util_ext.c	(revision 1719)
+++ /inundation/pmesh/util_ext.c	(revision 1719)
@@ -0,0 +1,130 @@
+// Python - C extension for finite_volumes util module.
+//
+// To compile (Python2.3):
+//  gcc -c util_ext.c -I/usr/include/python2.3 -o util_ext.o -Wall -O 
+//  gcc -shared util_ext.o  -o util_ext.so	
+//
+// See the module util.py
+//
+//
+// Ole Nielsen, GA 2004
+	
+#include "Python.h"
+#include "Numeric/arrayobject.h"
+#include "math.h"
+
+//Shared snippets
+#include "util_ext.h"
+
+
+
+int _point_on_line(double x, double y, 
+		   double x0, double y0, 
+		   double x1, double y1) {
+  /*Determine whether a point is on a line segment 
+   
+    Input: x, y, x0, x0, x1, y1: where
+        point is given by x, y
+	line is given by (x0, y0) and (x1, y1)
+   	
+  */
+    
+  double a0, a1, a_normal0, a_normal1, b0, b1, len_a, len_b;
+  
+  a0 = x - x0;
+  a1 = y - y0; 
+  
+  a_normal0 = a1;
+  a_normal1 = -a0;
+           		
+  b0 = x1 - x0;
+  b1 = y1 - y0;
+    
+  if ( a_normal0*b0 + a_normal1*b1 == 0 ) {   
+    //Point is somewhere on the infinite extension of the line
+
+    len_a = sqrt(a0*a0 + a1*a1);    	       
+    len_b = sqrt(b0*b0 + b1*b1);
+        	       	                
+    if (a0*b0 + a1*b1 >= 0 && len_a <= len_b) {
+      return 1;
+    } else {
+      return 0;
+    }
+  } else {
+    return 0;
+  }
+}
+
+
+
+// Gateways to Python
+PyObject *point_on_line(PyObject *self, PyObject *args) {
+  //
+  // point_on_line(x, y, x0, y0, x1, y1)
+  //
+  
+  double x, y, x0, y0, x1, y1;
+  int res;
+  PyObject *result;
+  
+  // Convert Python arguments to C  
+  if (!PyArg_ParseTuple(args, "dddddd", &x, &y, &x0, &y0, &x1, &y1))  
+    return NULL;
+
+  
+  // Call underlying routine
+  res = _point_on_line(x, y, x0, y0, x1, y1);
+
+  // Return values a and b
+  result = Py_BuildValue("i", res);
+  return result;
+}
+
+
+PyObject *gradient(PyObject *self, PyObject *args) {
+  //
+  // a,b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
+  //
+  
+  double x0, y0, x1, y1, x2, y2, q0, q1, q2, a, b;
+  PyObject *result;
+  
+  // Convert Python arguments to C  
+  if (!PyArg_ParseTuple(args, "ddddddddd", &x0, &y0, &x1, &y1, &x2, &y2,
+			&q0, &q1, &q2))  
+    return NULL;
+
+  
+  // Call underlying routine
+  _gradient(x0, y0, x1, y1, x2, y2, 
+	    q0, q1, q2, &a, &b);
+
+  // Return values a and b
+  result = Py_BuildValue("dd", a, b);
+  return result;
+}
+
+
+// Method table for python module
+static struct PyMethodDef MethodTable[] = {
+  /* The cast of the function is necessary since PyCFunction values
+   * only take two PyObject* parameters, and rotate() takes
+   * three.
+   */
+  
+  //{"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},  
+  {"gradient", gradient, METH_VARARGS, "Print out"},    
+  {"point_on_line", point_on_line, METH_VARARGS, "Print out"},      
+  {NULL, NULL, 0, NULL}   /* sentinel */
+};
+
+
+
+// Module initialisation   
+void initutil_ext(void){
+  Py_InitModule("util_ext", MethodTable);
+  
+  import_array();     //Necessary for handling of NumPY structures  
+}
+
Index: /inundation/pmesh/util_ext.h
===================================================================
--- /inundation/pmesh/util_ext.h	(revision 1719)
+++ /inundation/pmesh/util_ext.h	(revision 1719)
@@ -0,0 +1,100 @@
+// Python - C extension for finite_volumes util module.
+//
+// To compile (Python2.3):
+//  gcc -c util_ext.c -I/usr/include/python2.3 -o util_ext.o -Wall -O 
+//  gcc -shared util_ext.o  -o util_ext.so	
+//
+// See the module util.py
+//
+//
+// Ole Nielsen, GA 2004
+	
+#include "Python.h"	
+#include "Numeric/arrayobject.h"
+#include "math.h"
+
+
+double max(double x, double y) {  
+  //Return maximum of two doubles
+  
+  if (x > y) return x;
+  else return y;
+}
+
+
+double min(double x, double y) {  
+  //Return minimum of two doubles
+  
+  if (x < y) return x;
+  else return y;
+}
+
+
+int _gradient(double x0, double y0, 
+	      double x1, double y1, 
+	      double x2, double y2, 
+	      double q0, double q1, double q2, 
+	      double *a, double *b) {
+
+  double det;
+  
+  det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0);
+
+  *a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0);
+  *a /= det;
+
+  *b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0);
+  *b /= det;
+
+  return 0;
+}
+
+
+void print_numeric_array(PyArrayObject *x) {  
+  int i, j;
+  for (i=0; i<x->dimensions[0]; i++) {  
+    for (j=0; j<x->dimensions[1]; j++) {
+      printf("%f ", *(double*) (x->data + i*x->strides[0] + j*x->strides[1]));
+    }
+    printf("\n");  
+  }
+  printf("\n");    
+}
+
+void print_numeric_vector(PyArrayObject *x) {  
+  int i;
+  for (i=0; i<x->dimensions[0]; i++) {
+    printf("%f ", *(double*) (x->data + i*x->strides[0]));  
+  }
+  printf("\n");  
+}
+
+PyArrayObject *get_consecutive_array(PyObject *O, char *name) {
+  PyArrayObject *A, *B;
+  
+
+  //Get array object from attribute
+  
+  /*
+  //FIXME: THE TEST DOESN't WORK
+  printf("Err = %d\n", PyObject_HasAttrString(O, name));
+  if (PyObject_HasAttrString(O, name) == 1) {
+    B = (PyArrayObject*) PyObject_GetAttrString(O, name);
+    if (!B) return NULL;
+  } else {
+    return NULL;
+    } 
+  */
+    
+  B = (PyArrayObject*) PyObject_GetAttrString(O, name);
+  if (!B) return NULL;     
+  
+  //Convert to consecutive array
+  A = (PyArrayObject*) PyArray_ContiguousFromObject((PyObject*) B, B -> descr -> type, 0, 0);
+  
+  Py_DECREF(B); //FIXME: Is this really needed??
+  
+  if (!A) return NULL;
+  return A;
+}
+
Index: /inundation/pmesh/visualmesh.py
===================================================================
--- /inundation/pmesh/visualmesh.py	(revision 1719)
+++ /inundation/pmesh/visualmesh.py	(revision 1719)
@@ -0,0 +1,302 @@
+import mesh
+from   tkSimpleDialog import Dialog,askfloat, askinteger, askstring
+from Tkinter import  FALSE,TRUE, Frame,X, LEFT,YES,BOTH,ALL,Widget,CURRENT, Label,W, Entry, E, ACTIVE, NORMAL, StringVar
+from tkMessageBox import showerror
+
+class vAbstract:
+    """
+    Define the visualisation of a mesh object
+    """
+    def __init__(self, association):
+        # The association is the class oncanvas will hold ie mesh.Vertex
+        self.association = association 
+        self.oncanvas = {} 
+
+    def visualise(self, meshobject, uniqueID, canvas, scale):
+        """
+        draws a mesh object and add it the the canvas data structure.
+        Note: a guiID attribute is added to each point object thats visualised.
+        This is used in zooming.
+
+        meshobject is from mesh.py, eg vertex, hole
+        """
+        meshobject.guiID = uniqueID 
+        meshobject.draw(canvas, uniqueID, scale =scale)
+        self.oncanvas[uniqueID] = meshobject
+        return meshobject
+
+    def unvisualise(self, meshobject, canvas):
+        """
+        delete a visual mesh object and
+        delete it from the canvas mesh data structure
+        """
+        canvas.delete(meshobject.guiID)
+        del self.oncanvas[meshobject.guiID]
+
+    def hasKey(self, key):
+        """
+        returns true if the key is in the canvas list
+        """
+        if self.oncanvas.has_key(key):
+            return True
+        else:
+            return False
+ 
+    def getMeshObject(self, key):
+        """
+        returns the mesh object,
+        given it's key.
+
+        precon:  The key is valid
+        """
+        return self.oncanvas[key]
+
+    def editWindow(self, canvas, selMeshObject,userMeshChanged):
+        print "Not implimented"
+        return userMeshChanged
+
+    def defaultWindow(self, canvas):
+        print "Not implimented"
+
+    def draw (self):
+        """
+        This method must be overriden
+        """
+        pass
+
+    def __add__(self, other):
+        return VMesh([self,other])
+
+    def __repr__(self):
+        return str(self.__class__)
+    
+class vPoints(vAbstract):
+    """
+    Define the visualisation of a point
+    """
+
+    def draw(self,x,y,mesh,uniqueID,scale,canvas,event):
+        """
+        draw a point object, plus add it to the mesh data structure
+
+        event isn't used
+        """
+        point = mesh.addUserPoint(self.association,x/scale,y/scale)
+        self.visualise(point, uniqueID, canvas, scale)
+        return point
+ 
+class vRegions(vPoints):
+    """
+    Define the visualisation of a region
+    """ 
+    def editWindow(self, canvas, selMeshObject,userMeshChanged):
+        dialog = EditRegionDialog (canvas,
+                                   selMeshObject.getTag(),
+                                   selMeshObject.getMaxArea() )
+        if dialog.ValueOk:
+            selMeshObject.setTag(dialog.tag)
+            if dialog.maxArea != 0.0: #using 0.0 to mean no max area (hacky)
+                selMeshObject.setMaxArea(dialog.maxArea)
+            else:
+                selMeshObject.deleteMaxArea()
+            userMeshChanged = True
+        return userMeshChanged
+            
+class EditRegionDialog(Dialog):
+    """
+    Dialog box for editing region info
+    """
+    # initial values, hard coded.
+    # should be values associated with the current mesh
+
+
+    def __init__(self,
+                 parent,
+                 tag,
+                 maxArea):
+
+
+        self.tag = tag
+        self.maxArea = maxArea
+
+        Dialog.__init__(self, parent)
+
+        
+    def body(self, master):
+        """
+        GUI description
+        """
+        self.title("Edit Region")
+        
+        Label(master,
+              text='tag:').grid(row=0, sticky=W)
+        Label(master,
+              text='Maximum Triangle Area').grid(row=1, sticky=W)
+
+
+        tagVar = StringVar()
+        tagVar.set(self.tag)
+        self.tagstr   = Entry(master,
+                                   width = 16,
+                                   textvariable = tagVar,
+                                   takefocus=1)
+
+    
+        maxAreaVar = StringVar()
+        if self.maxArea == None:
+          self.maxArea = ""  
+        maxAreaVar.set(self.maxArea)
+        self.maxAreastr   = Entry(master,
+                                   width = 16,
+                                   textvariable = maxAreaVar,
+                                   takefocus=1)
+
+        self.tagstr.grid(row=0, column=1, sticky=W)
+        self.maxAreastr.grid(row=1, column=1, sticky=W)
+
+        self.tag  = -1
+        self.maxArea  = -1
+        self.ValueOk = False
+
+
+    def apply(self):
+        """
+        check entered values
+        """
+        self.goodMaxArea = self.goodtag = True
+        self.ValueOk = True
+        try:
+            self.tag = self.tagstr.get()
+        except ValueError:
+            self.ValueOk = False
+            showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+
+        if not self.maxAreastr.get() == "":
+            try:    
+                self.maxArea = float(self.maxAreastr.get())
+                #MeshGenDialog.lastMaxArea =self.maxArea
+            except ValueError:
+                self.ValueOk = False
+                showerror('Bad mesh generation values',
+                                   ' Values are not numbers.')
+        else:
+            self.maxArea = 0.0
+
+        try: 
+            # value checking
+            if self.goodMaxArea == True and self.maxArea < 0.0:
+                raise IOError
+            
+        except IOError:
+            self.ValueOk = False
+            showerror('Bad mesh generation values',
+                                   'Values are out of range.')
+
+       
+class vSegments(vAbstract):
+    """
+    Define the visualisation of a segment
+    """
+
+    def draw(self,v1,v2,mesh,uniqueID,scale,canvas,event):
+        """
+        draw a segment object, plus add it to the mesh data structure
+
+        event isn't used
+        """
+        segment = mesh.addUserSegment(v1,v2)
+        self.visualise(segment, uniqueID, canvas, scale)
+        return segment    
+  
+    def editWindow_int(self, canvas, selMeshObject):
+        ask_int = askinteger("Edit Segment", "Tag",
+                             minvalue=0, initialvalue= selMeshObject.tag)
+        if ask_int >= 0:
+            selMeshObject.tag = ask_int
+            
+    def editWindow(self, canvas, selMeshObject, userMeshChanged):
+        ask_string = askstring("Edit Segment Tag", "Tag",
+                              initialvalue= str(selMeshObject.tag))
+        if ask_string != None:
+            selMeshObject.tag = ask_string
+            userMeshChanged = True
+        return userMeshChanged
+    
+    def defaultWindow(self, canvas):
+        ask_string = askstring("Edit Default Segment Tag", "Tag",
+                     initialvalue= str(self.association.get_default_tag()))
+        if ask_string != None:
+            self.association.set_default_tag(ask_string)
+        
+class vTriangles(vAbstract):
+    """
+    Define the visualisation of a triangle
+    """
+
+    def draw(self,mesh,uniqueID,scale,canvas,event):
+        """
+        currently triangles have no draw function
+        """
+        raise TypeError
+
+
+class vMesh(vAbstract):
+    """
+    Define the visualisation of a collection of mesh object dictionaries (eg vSegments)
+    """
+    def __init__(self,MeshObjects):
+        self.association = mesh.MeshObject
+        self.MeshObjectsList = MeshObjects
+
+    def visualise(self, meshobject, uniqueID, canvas, scale):
+        """
+        This should find the right vAbstract instance and call visualise
+        """
+        raise Exception
+
+    def unvisualise(self, MeshObject, canvas):
+        """
+        delete a visual object and
+        delete it from the canvas mesh data structure
+        """
+        for MeshObjects in self.MeshObjectsList:
+            if isinstance(MeshObject,MeshObjects.association):
+                MeshObjects.unvisualise(MeshObject, canvas)
+                break
+            
+    def hasKey(self, key):
+        """
+        returns true if the key is in the canvas list
+        """
+        for MeshObjects in self.MeshObjectsList:
+            if MeshObjects.oncanvas.has_key(key):
+                return True
+        return False
+
+    def getMeshObject(self, key):
+        """
+        returns the mesh object,
+        given it's key.
+
+        precon:  The key is valid
+        """
+        for MeshObjects in self.MeshObjectsList:
+            try:
+                return MeshObjects.oncanvas[key]
+            except KeyError:
+                pass
+        raise KeyError
+
+    def editWindow(self, canvas, selMeshObject, userMeshChanged):
+        for MeshObjects in self.MeshObjectsList:
+            if isinstance(selMeshObject,MeshObjects.association):
+                userMeshChanged =MeshObjects.editWindow(canvas, selMeshObject,userMeshChanged)
+                break
+        return userMeshChanged
+    
+    def defaultWindow(self, canvas, MeshClass):
+        for MeshObjects in self.MeshObjectsList:
+            if MeshClass == MeshObjects.association:
+                MeshObjects.defaultWindow(canvas)
+                break 
