source: inundation/ga/storm_surge/pmesh/pmesh.py @ 1501

Last change on this file since 1501 was 1501, checked in by duncan, 20 years ago

changed the hacky addCylinders method. Uncomment in line 1462 to draw two cylinders.

File size: 75.4 KB
Line 
1
2import  Pmw, AppShell, math, time, string, marshal
3from toolbarbutton import ToolBarButton
4import tkFileDialog
5from   tkSimpleDialog import Dialog
6import mesh
7from Tkinter import  FALSE,TRUE, Frame,X, LEFT,YES,BOTH,ALL,Widget,CURRENT, Label,W, Entry, E, StringVar, END, Checkbutton, Radiobutton, IntVar, DISABLED, NORMAL
8#from cursornames import TLC,TRC, BLC, BRC, TS, RS, LS, BS
9from tkMessageBox import showerror, _show, QUESTION,YESNOCANCEL
10import types
11import visualmesh
12import os
13import profile
14import load_mesh.loadASCII
15from alpha_shape.alpha_shape import AlphaError
16
17# CONSTANTS
18VERT_SELECT_ADDING_SEG_COLOR = 'orange'
19SELECT_COLOR = 'red'
20SEG_COLOUR = 'blue'
21TRIANGLE_COLOUR = 'green'
22APPLICATION_NAME = 'Pmesh'
23SET_COLOUR = 'red'
24DEFAULT_ATTRIBUTE = 'elevation'
25
26#for alpha shapes
27NO_SELECTION = 0
28AUTO = 1
29SET_ALPHA = 2
30
31
32class Draw(AppShell.AppShell):
33    usecommandarea = 1
34    appname        = APPLICATION_NAME
35    frameWidth     = 840
36    frameHeight    = 600
37   
38   
39    def createButtons(self):
40        """
41        Add buttons to the bottom of the GUI
42        """
43        self.buttonAdd('Postscript',
44              helpMessage='Save current drawing (as PostScript)',
45              statusMessage='',
46              command=self.ipostscript)
47        self.buttonAdd('Clear', helpMessage='Delete the mesh',
48              statusMessage='', command=self.clearMesh)
49        self.buttonAdd('Close', helpMessage='Close Screen',
50              statusMessage='', command=self.close)
51       
52    def createBase(self):
53        """
54        Create the GUI framework.  Set up the GUI
55        """
56        self.toolbar = self.createcomponent('toolbar', (), None,
57                  Frame, (self.interior(),), background="gray90")
58        self.toolbar.pack(fill=X)
59
60        self.scrolledcanvas =  self.createcomponent('ScrolledCanvas', (), None,
61                                       Pmw.ScrolledCanvas, (self.interior(),)
62                                       ,borderframe = 1
63                                       ,labelpos = 'n'
64                                       )
65        self.scrolledcanvas.configure(hscrollmode = 'dynamic')
66        self.scrolledcanvas.configure(vscrollmode = 'dynamic')
67        self.scrolledcanvas.pack(side=LEFT, expand=YES, fill=BOTH)
68        self.canvas = self.scrolledcanvas.component('canvas')
69        self.canvas.configure( background="white" )
70       
71        self.canvas.pack(side=LEFT, expand=YES, fill=BOTH)
72
73        Widget.bind(self.canvas, "<Button-1>", self.mouseDown)
74        Widget.bind(self.canvas, "<Button3-ButtonRelease>", self.rightMouseUp)
75        Widget.bind(self.canvas, "<Button2-ButtonRelease>",self.DeleteSelectedMeshObject)
76        # "<Delete>" didn't work..
77        #Widget.bind(self.canvas, "<Delete>", self.DeleteSelectedMeshObject)
78
79        #self.root.bind("<KeyPress>", self.setRegular)
80        #self.root.bind("<KeyRelease>", self.setRegular)
81       
82        self.scrolledcanvas.resizescrollregion()
83
84#     def setRegular(self, event):
85#         if event.type == '2' and event.keysym == 'Shift_L':
86#             self.regular = TRUE
87#         else:
88#             self.regular = FALSE
89
90    def createMenus(self):
91        """
92        Add menus to the top of the GUI
93        """
94        self.menuBar.deletemenuitems('File',0)
95        self.menuBar.addmenuitem('File', 'command', 'New mesh',
96                                 label='New', command=self.clearMesh)
97        self.menuBar.addmenuitem('File', 'command', 'Open mesh',
98                                 label='Open...', command=self.importFile)
99        self.menuBar.addmenuitem('File', 'command', 'Save mesh',
100                                 label='Save', command=self.saveDrawing)
101        self.menuBar.addmenuitem('File', 'command', 'Save mesh',
102                                 label='SaveAs...', command=self.saveAsDrawing)
103       
104        self.menuBar.addmenuitem('File', 'separator')
105        self.menuBar.addmenuitem('File', 'command',
106                                 'Add ungenerated file from arcGIS',
107                                 label='Add ungenerated file...',
108                                 command=self.ImportUngenerate)
109       
110        self.menuBar.addmenuitem('File', 'command',
111                                 'Export ASCII obj',
112                                 label='Export ASCII obj',
113                                 command=self.exportObj)
114       
115        self.menuBar.addmenuitem('File', 'command',
116                                 'Export ASCII segment outline',
117                                 label='Export ASCII segment outline...',
118                                 command=self.exportASCIIsegmentoutlinefile)
119       
120        self.menuBar.addmenuitem('File', 'command',
121                                 'Export ASCII xya file',
122                                 label='Export ASCII xya file...',
123                                 command=self.exportPointsFile)
124       
125        self.menuBar.addmenuitem('File', 'command',
126                                 'add Segments to connect all vertices'  ,
127                                 label='join vertices',
128                                 command=self.joinVertices)
129        self.menuBar.addmenuitem('File', 'command',
130                                 'add Segments to form alpha shape'  ,
131                                 label='Auto segment',
132                                 command=self.autoSegment)
133        self.menuBar.addmenuitem('File', 'command',
134                           'modify the alpha boundary by applying filters',
135                                 label='filter alpha boundary',
136                                 command=self.autoSegmentFilter)
137        self.menuBar.addmenuitem('File', 'command', 'Normalise mesh',
138                                 label='Normalise mesh', command=self.normaliseMesh)
139        self.menuBar.addmenuitem('File', 'command', 'Normalise mesh for glutobj',
140                                 label='Normalise mesh for glutobj', command=self.normalise4ObjMesh)
141        self.menuBar.addmenuitem('File', 'separator')
142        self.menuBar.addmenuitem('File', 'command', '',
143                                 label='Print geo reference', command=self.printGeoReference)
144        self.menuBar.addmenuitem('File', 'separator')
145        self.menuBar.addmenuitem('File', 'command', 'Exit program',
146                                 label='Exit', command=self.quit)
147
148    def createTools(self):
149        """
150        Add buttons to the top of the GUI
151        """
152        self.mouseDownFunc = {}
153        self.modeClass = {}
154        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif',
155                      width=10, state='disabled')
156        for key, balloon, mouseDownFunc, Mode in [
157            ('pointer','Edit drawing eventually.  Right now this does nothing', self.drag, None)
158            ,('vertex',    'Vertex mode', self.drawVertex, mesh.Vertex)
159            ,('segment', 'Segment mode',self.selectSegmentPoint, mesh.Segment)
160            ,('hole', 'hole mode',self.drawHole, mesh.Hole)
161            ,('region', 'region mode',self.drawRegion, mesh.Region)
162            ]:
163            t = ToolBarButton(self, self.toolbar, key, '%s.gif' % key,
164                          command=self.selectFunc, balloonhelp=balloon,
165                               statushelp='')
166            t.cycle("DrawMode")
167            if key == 'pointer': #FIXME- this is specified in line 1062 as well
168                                 # self.selectFunc('pointer')
169                self.curFunc  = self.drawVertex
170                t.setInitialSunkenButton("DrawMode")
171            self.modeClass[key] = Mode
172            # for actions that occur when the mouse goes down
173            self.mouseDownFunc[key] = mouseDownFunc
174         
175    def createZooms(self):
176        """
177        Add zoom buttons to the top of the GUI
178        """
179        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
180                      state='disabled')
181        zoom = '0.5'
182        ToolBarButton(self, self.toolbar, zoom, 'zoom%s.gif' %
183                      zoom, command=self.selectZoom,
184                      balloonhelp='*%s zoom' % zoom,
185                      statushelp='')
186           
187        ToolBarButton(self, self.toolbar,'1.0', 'zoomToMesh.gif',
188                      command=self.ResizeToFitWrapper,
189                      balloonhelp='Zooms to mesh size',
190                      statushelp='')
191        zoom = '2'
192        ToolBarButton(self, self.toolbar, zoom, 'zoom%s.gif' %
193                      zoom, command=self.selectZoom,
194                      balloonhelp='*%s zoom' % zoom,
195                      statushelp='')
196
197    def createEdits(self):
198        """
199        Add Edit buttons to the top of the GUI
200        """
201        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
202                      state='disabled')
203        for key, func, balloon in [
204                ('addVertex', self.windowAddVertex, 'add Vertex'),
205                ('edit', self.windowEdit, 'edit selected object'),
206                ('default', self.windowDefault, 'set default value for selected mode'),
207                ('joinVer', self.joinVerticesButton, 'add Segments to connect all vertices'),     
208             #   ('autoSegHull', self.autoSegmentHullButton, 'add Segments to form alpha shape, using Hull'), 
209             #   ('autoSeg', self.autoSegmentButton, 'add Segments to form alpha shape'),
210                ('autoSegGiveAlpha', self.autoSegmentGiveAlphaButton, 'add Segments to form alpha shape, specify alpha'),
211                ('meshGen', self.windowMeshGen, 'Generate Mesh')]:
212            ToolBarButton(self, self.toolbar, key, '%s.gif' % key,
213                          command=func, balloonhelp=balloon,
214                               statushelp='' )
215
216
217
218    def createSetTools(self):
219        """
220        Add set tool buttons to the top of the GUI
221        """
222        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
223                      state='disabled')
224        for key, func, balloon in [
225                ('threshold', self.threshold, 'threshold the set'),
226                ('Courant_threshold', self.Courant_threshold, 'Courant_threshold the set'),
227                ('gradient_threshold', self.gradient_threshold, 'gradient_threshold the set'),
228                ('smooth', self.smooth_polySet, 'smooth the polygons'),
229                ('polyset', self.triangles_to_polySet, 'make a poly set out of selected triangles')]:     
230                #('refineSet', self.refineSet, 'Refine the set')]:
231            ToolBarButton(self, self.toolbar, key, '%s.gif' %key,
232                          command=func, balloonhelp=balloon,
233                               statushelp='' )
234
235    def createSetIcons(self):
236        """
237        Add Edit buttons to the top of the GUI
238        """
239        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
240                      state='disabled')
241        for key, func, balloon in [
242                ('selectAllTriangles', self.selectAllTriangles, 'select all'),
243                ('none', self.clearSelection, 'clear selection')]:
244            ToolBarButton(self, self.toolbar, key, '%s.gif' %key,
245                          command=func, balloonhelp=balloon,
246                               statushelp='' )
247
248    def createVisualiseIcons(self):
249        """
250        Add Edit buttons to the top of the GUI
251        """
252        ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10,
253                      state='disabled')
254        for key, func, balloon in [
255                ('visualise', self.visualise, 'Visualise mesh triangles'),
256                ('unvisualise', self.unvisualise, 'Do not visualise mesh triangles (for large meshes)')]:
257            ToolBarButton(self, self.toolbar, key, '%s.gif' %key,
258                          command=func, balloonhelp=balloon,
259                               statushelp='' )
260
261
262    def refineSet(self,parent):
263        self.mesh.refineSet(self.selSet)
264        self.visualiseMesh(self.mesh)
265
266
267    def setStructureNumber(self,parent):
268        dialog =  setStructureNumberDialog(self.canvas)
269        if dialog.numberOK:
270            self.structureSize = dialog.number
271
272    def erode(self, parent):
273#Not implimented
274        self.canvas.delete(ALL)
275        self.mesh.erode(self.selSet,structureSize=self.structureSize)
276        self.visualiseMesh(self.mesh)
277
278
279    def dilate(self, parent):
280#Not implimented
281        self.canvas.delete(ALL)
282        self.mesh.dilate(self.selSet,structureSize=self.structureSize)
283        self.visualiseMesh(self.mesh)
284
285    def general_threshold(self,parent,function,function_description):
286        """
287        add a vertex using a window and entering x y values.
288
289        the parent attribute isn't used by this function.
290        need to userstand toolbarbutton.py to know how to
291        get rid of it.
292        """
293        if self.selSet == 'None':
294            self.selectAllTriangles(parent)
295
296        dialog = GeneralThresholdDialog(self.canvas,self.mesh.attributeTitles,function_description)
297        if dialog.minmaxValuesOk:
298            self.canvas.delete(ALL)
299            min = dialog.min
300            max = dialog.max
301            attribute_name = dialog.attribute_name
302            self.mesh.general_threshold(self.selSet,min=min,max=max,attribute_name = attribute_name,function=function)
303            self.visualiseMesh(self.mesh)
304
305    def threshold(self, parent):
306        """
307        add a vertex using a window and entering x y values.
308
309        the parent attribute isn't used by this function.
310        need to userstand toolbarbutton.py to know how to
311        get rid of it.
312        """
313        function = self.mesh.av_att
314        function_description = 'average attribute of triangle'
315        self.general_threshold(parent,function,function_description)
316
317
318    def Courant_threshold(self, parent):
319        """
320        add a vertex using a window and entering x y values.
321
322        the parent attribute isn't used by this function.
323        need to userstand toolbarbutton.py to know how to
324        get rid of it.
325        """
326        function = self.mesh.Courant_ratio
327        function_description = 'average attribute/area of triangle'
328        self.general_threshold(parent,function,function_description)
329
330    def gradient_threshold(self, parent):
331        """
332        add a vertex using a window and entering x y values.
333
334        the parent attribute isn't used by this function.
335        need to userstand toolbarbutton.py to know how to
336        get rid of it.
337        """
338        function = self.mesh.Gradient
339        function_description = 'average gradient of triangle'
340        self.general_threshold(parent,function,function_description)
341
342    def smooth_polySet(self,parent):
343        dialog = SmoothDialog(self.canvas)
344        if dialog.valueOK:
345            min_radius = dialog.min_radius
346            self._smooth_polySet(min_radius)
347
348    def _smooth_polySet(self,min_radius):
349        userVertices,userSegments,alphaSegments = \
350            self.mesh.smooth_polySet(min_radius=min_radius)
351
352        self.mesh.userVertices=[]
353        self.mesh.userSegments=[]
354        self.mesh.alphaSegments=[]
355        self.canvas.delete(ALL)
356        event = None
357        print 'len(userVertices.keys())'
358        print len(userVertices.keys())
359        print 'len(userSegments.keys())'
360        print len(userSegments.keys())
361        print 'len(alphaSegments.keys())'
362        print len(alphaSegments.keys())
363
364        #######
365        point_keys = {}
366        for vert in userVertices.keys():
367            assert not point_keys.has_key((vert.x,vert.y))
368            point_keys[(vert.x,vert.y)]=vert
369        assert len(point_keys.keys())==len(userVertices.keys())
370        #######
371
372        for v in userVertices.keys():
373            x = v.x*self.SCALE
374            y = v.y*self.SCALE
375            userVertices[(v.x,v.y)]=self.drawVertex(x,y,event)
376
377        for line in userSegments.keys():
378            v0 = userVertices[line[0]]
379            v1 = userVertices[line[1]]
380            segment = self.drawSegment(v0,v1)
381            segment.set_tag(userSegments[line].tag)
382
383        for line in alphaSegments.keys():
384            v0 = userVertices[line[0]]
385            v1 = userVertices[line[1]]
386            segment = self.drawSegment(v0,v1)
387            segment.set_tag(alphaSegments[line].tag)
388        self.visualiseMesh(self.mesh)
389
390
391    def triangles_to_polySet(self,parent):
392        userVertices,userSegments,alphaSegments = \
393            self.mesh.triangles_to_polySet(self.selSet)
394
395        self.mesh.userVertices=[]
396        self.canvas.delete(ALL)
397
398        event = None
399        print 'len(userVertices.keys())'
400        print len(userVertices.keys())
401        print 'len(userSegments.keys())'
402        print len(userSegments.keys())
403        print 'len(alphaSegments.keys())'
404        print len(alphaSegments.keys())
405
406
407        #######
408        point_keys = {}
409        for vert in userVertices.keys():
410            assert not point_keys.has_key((vert.x,vert.y))
411            point_keys[(vert.x,vert.y)]=vert
412        assert len(point_keys.keys())==len(userVertices.keys())
413        #######
414
415        for v in userVertices.keys():
416            if userVertices[v] is True:
417                x = v.x*self.SCALE
418                y = v.y*self.SCALE
419                userVertices[(v.x,v.y)]=self.drawVertex(x,y,event)
420
421        for line in userSegments.keys():
422            v0 = userVertices[line[0]]
423            v1 = userVertices[line[1]]
424            segment = self.drawSegment(v0,v1)
425            segment.set_tag(userSegments[line].tag)
426
427        for line in alphaSegments.keys():
428            v0 = userVertices[line[0]]
429            v1 = userVertices[line[1]]
430            segment = self.drawSegment(v0,v1)
431            segment.set_tag(alphaSegments[line].tag)
432        self.visualiseMesh(self.mesh)
433        #self.smooth_polySet(parent)
434
435    def selectTriangles(self,setName):
436        """
437        """
438        self.canvas.delete(ALL)
439        self.selSet = setName
440        self.visualiseMesh(self.mesh)
441
442    def selectAllTriangles(self,parent):
443        """
444        selected all triangles in the mesh
445        """
446        self.canvas.delete(ALL)
447        self.selSet = self.mesh.selectAllTriangles()
448        self.visualiseMesh(self.mesh)
449
450    def clearSelection(self,parent):
451    #FIXME looks like self.clearSelections - change name (Peter)
452        """
453        """
454        self.canvas.delete(ALL)
455        self.selSet = self.mesh.clearSelection()
456        self.visualiseMesh(self.mesh)
457
458    def visualise(self,parent):
459        self.canvas.delete(ALL)
460        self.Visualise = True
461        self.visualiseMesh(self.mesh)
462
463    def unvisualise(self,parent):
464        self.canvas.delete(ALL)
465        self.Visualise = False
466        self.visualiseMesh(self.mesh)
467
468    def createMesh(self):
469        """
470        Build the data structures for storing the mesh objects
471        """
472        self.mesh = mesh.Mesh()
473       
474        self.Vertices = visualmesh.vPoints(mesh.Vertex)
475        self.Segments = visualmesh.vSegments(mesh.Segment)
476        self.Holes = visualmesh.vPoints(mesh.Hole)
477        self.Regions = visualmesh.vRegions(mesh.Region)
478        self.UserMesh = visualmesh.vMesh([self.Vertices,self.Segments,self.Holes,self.Regions])
479       
480        self.Triangles = visualmesh.vTriangles(mesh.Triangle)
481        self.selSet='None'
482
483
484    def deleteMesh(self):
485        """
486        Delete the data structures for storing the mesh objects
487        """
488        self.mesh = None
489        self.Vertices = None
490        self.Segments = None
491        self.Triangles = None
492       
493    def addCylinders(self):
494        """
495        Automatically add some verts and segs to the mesh.Used in Debugging
496        """
497        from coordinate_transforms.geo_reference import Geo_reference,DEFAULT_ZONE
498        offset_x = 30
499        offset_y = 40
500        v1 = self.drawVertex(0-offset_x,0-offset_y,None)
501        v2 = self.drawVertex(50-offset_x,2-offset_y,None)
502        v3 = self.drawVertex(70-offset_x,30-offset_y,None)
503        v4 = self.drawVertex(25-offset_x,50-offset_y,None)
504        v5 = self.drawVertex(-10-offset_x,20-offset_y,None)
505        #v5 = self.drawVertex(25,50,None)
506        s1 = self.drawSegment(v1,v2)
507        s1.set_tag("s1")
508        s2 = self.drawSegment(v2,v3)
509        s2.set_tag("s2")
510        s3 = self.drawSegment(v3,v4)
511        s3.set_tag("s3")
512        s4 = self.drawSegment(v4,v5)
513        s4.set_tag("s4")
514        s5 = self.drawSegment(v1,v5)
515        s5.set_tag("s5")
516
517        x_origin = 10-offset_x
518        y_origin = 20-offset_y
519        r = 10
520        pi = math.pi
521        num_of_cuts = 100
522        cuts = []
523        factor = 2* math.pi/num_of_cuts
524        for cut in range(num_of_cuts):
525             cuts.append(cut*factor)
526       
527        for radius in cuts:
528            x = x_origin + r * math.cos(radius)
529            y = y_origin + r * math.sin(radius)
530            v = self.drawVertex(x,y,None)
531            if not radius == 0.0:   # FIXME
532                self.drawSegment(v,v_old)
533            else:
534                v_first = v
535            v_old = v
536        self.drawSegment(v,v_first)
537        region = self.drawRegion(x_origin, y_origin, 0)
538        region.setTag("setheight5")
539       
540       
541        x_origin = 30-offset_x
542        y_origin = 30-offset_y
543        r = 5
544        pi = math.pi
545        num_of_cuts = 100
546        cuts = []
547        factor = 2* math.pi/num_of_cuts
548        for cut in range(num_of_cuts):
549             cuts.append(cut*factor)
550       
551        for radius in cuts:
552            x = x_origin + r * math.cos(radius)
553            y = y_origin + r * math.sin(radius)
554            v = self.drawVertex(x,y,None)
555            if not radius == 0.0:   # FIXME
556                self.drawSegment(v,v_old)
557            else:
558                v_first = v
559            v_old = v
560        self.drawSegment(v,v_first)
561        region = self.drawRegion(x_origin, y_origin, 0)
562        region.setTag("setheight10")
563        geo_reference = Geo_reference(zone=DEFAULT_ZONE,
564                                xllcorner=offset_x,
565                                yllcorner=offset_x)
566        ###self.mesh.geo_reference(zone=56,
567           #                     xllcorner=offset_x,
568            #                    yllcorner=offset_x)
569           
570        #Since the new vertex may be off screen
571        self.scrolledcanvas.resizescrollregion()
572
573        # generate the mesh
574        minAngle = 30.0
575        numTriangles = 20000
576        self.clearSelections()
577        self.canvas.delete(ALL)
578        self.mesh = self.MeshGenAreaNumTriangles (minAngle,
579                                          numTriangles,
580                                          self.mesh)
581        self.UserMeshChanged = False
582        self.visualiseMesh(self.mesh)
583        print "Mesh Generation finished"
584           
585    def selectFunc(self, tag):
586        """
587        Change the current mode class
588        When changing from one mode to another
589        """
590        self.mouseDownCurFunc = self.mouseDownFunc[tag]
591        self.curModeClass = self.modeClass[tag]
592        self.clearSelections()
593        self.canvas.config(cursor='arrow')
594#        I can make it arrow, but it will change back to pointer, after
595#        adding an object
596#        if self.curFunc == self.func['pointer']:
597#            self.canvas.config(cursor='arrow')
598#         else:
599#             self.canvas.config(cursor='crosshair')
600
601    def clearSelections(self):
602        """
603        deselect objects that have been selected
604        """
605        if self.selMeshObject:
606            self.deselectMeshObject(self.selMeshObject, self.selMeshTag)       
607        if self.selVertex:
608            self.deselectVertex(self.selVertex, self.selVertexTag)
609         
610       
611    def selectZoom(self, tag):
612        """
613        Zoom in or out of the current mesh view
614        """
615        fraction = string.atof(tag)
616        self.SCALE *= fraction
617        self.scrolledcanvas.scale(ALL, 0, 0, fraction, fraction)
618
619        # Redraw all of the vertices, holes and regions,
620        #so the squares representing vertices
621        # don't get bigger
622        vertices = self.mesh.getUserVertices()
623        holes = self.mesh.getHoles()
624        regions = self.mesh.getRegions()
625        MeshObjects  = vertices + holes + regions
626
627        # make a list of tags to delete
628        guiIDs = [getattr(MeshObjects[i],'guiID') for i in xrange(len(MeshObjects))]
629        apply(self.canvas.delete, guiIDs)
630        for obj in MeshObjects:
631            if self.selVertex == obj:
632                obj.draw(self.canvas,obj.guiID,  scale =self.SCALE ,colour= VERT_SELECT_ADDING_SEG_COLOR)
633            elif self.selMeshObject == obj:
634                obj.draw(self.canvas,obj.guiID,  scale =self.SCALE ,colour= SELECT_COLOR)
635            else:
636                obj.draw(self.canvas,obj.guiID,  scale =self.SCALE ) 
637        top, bottom = self.scrolledcanvas.xview()
638        xcenter  = (top + bottom)/2
639        xdiff =  xcenter - top 
640        xcnew = xcenter - xdiff/fraction
641       
642        top, bottom = self.scrolledcanvas.yview()
643        ycenter = (top + bottom)/2
644        ydiff = ycenter - top
645        ycnew = ycenter - ydiff/fraction
646       
647        self.scrolledcanvas.resizescrollregion()
648        # update so the moveto calls will work...
649        self.scrolledcanvas.update()
650        # but calling update now does make things jerky
651        self.canvas.xview_moveto(xcnew)
652        self.canvas.yview_moveto(ycnew)
653       
654   
655    def windowAddVertex (self, parent):
656        """
657        add a vertex using a window and entering x y values.
658
659        the parent attribute isn't used by this function.
660        need to userstand toolbarbutton.py to know how to
661        get rid of it.
662        """
663       
664        dialog = AddVertexDialog(self.canvas)
665        if dialog.xyValuesOk:
666            print dialog.x
667            print dialog.y
668            self.drawVertex(dialog.x*self.SCALE,dialog.y*self.SCALE,None)
669            #Since the new vertex may be off screen
670            self.ResizeToFit()
671        else:
672            print "bad values"
673   
674    def windowDefault (self, parent):
675        """
676       
677        the parent attribute isn't used by this function.
678        need to userstand toolbarbutton.py to know how to
679        get rid of it.
680        """
681        # self.UserMesh is a vMesh instance
682        self.UserMesh.defaultWindow(self.canvas, self.curModeClass)
683   
684    def windowEdit (self, parent):
685        """
686
687        the parent attribute isn't used by this function.
688        need to userstand toolbarbutton.py to know how to
689        get rid of it.
690        """
691        if self.selMeshObject:   
692            self.UserMeshChanged = self.UserMesh.editWindow(self.canvas,
693                                     self.selMeshObject,
694                                     self.UserMeshChanged)
695   
696    def autoSegmentButton (self, parent):
697        self.autoSegment()
698
699       
700    def autoSegmentGiveAlphaButton (self, parent):
701        dialog = AutoSegmentDialog(self.canvas, self.meshLastAlpha)
702        if dialog.use_optimum.get() == SET_ALPHA:
703            if dialog.alphaValueOk:
704                self.autoSegment(alpha = dialog.alpha,
705                                 raw_boundary=dialog.raw_boundary.get(),
706                                 remove_holes=dialog.remove_holes.get(),
707                                 smooth_indents=dialog.smooth_indents.get(),
708                                 expand_pinch=dialog.expand_pinch.get())
709            else:
710                 showerror('pMesh',
711                      'Bad alpha value.')
712        else:
713            self.autoSegment(raw_boundary=dialog.raw_boundary.get(),
714                             remove_holes=dialog.remove_holes.get(),
715                             smooth_indents=dialog.smooth_indents.get(),
716                             expand_pinch=dialog.expand_pinch.get())
717           
718       
719    def autoSegment (self, alpha = None,
720                                 raw_boundary=True,
721                                 remove_holes=False,
722                                 smooth_indents=False,
723                                 expand_pinch=False ):
724        """
725        add Segments to bound all vertices
726       
727        """
728        if len(self.mesh.getUserVertices()) >= 3:
729            try:
730                newsegs, ObjectsToVisuallyDelete, self.meshLastAlpha = \
731                     self.mesh.autoSegment(alpha=alpha,
732                                           remove_holes=remove_holes,
733                                           smooth_indents=smooth_indents,
734                                           expand_pinch=expand_pinch)
735            except AlphaError:
736                showerror('pMesh',
737                          'Unable to autosegment.')
738            else:
739
740                for drawOb in ObjectsToVisuallyDelete:
741                    self.UserMesh.unvisualise(drawOb, self.canvas)
742               
743                for segment in newsegs:
744                    self.serial +=1
745                    self.uniqueID = 'M*%d' % self.serial
746                    self.Segments.visualise(segment,
747                                            self.uniqueID,
748                                            self.canvas,
749                                            self.SCALE)
750           
751        else:
752            showerror('pMesh',
753                      'Three or more vetices are needed to be able to autosegment.')
754
755
756    def autoSegmentFilter (self):
757        dialog = AutoSegmentFilterDialog(self.canvas)
758        newsegs, ObjectsToVisuallyDelete, self.meshLastAlpha = \
759                 self.mesh.autoSegmentFilter(raw_boundary=dialog.raw_boundary.get(),
760                             remove_holes=dialog.remove_holes.get(),
761                             smooth_indents=dialog.smooth_indents.get(),
762                             expand_pinch=dialog.expand_pinch.get())
763        #print "newsegs",newsegs
764        #print "ObjectsToVisuallyDelete",ObjectsToVisuallyDelete
765           
766        for drawOb in ObjectsToVisuallyDelete:
767            self.UserMesh.unvisualise(drawOb, self.canvas)
768               
769        for segment in newsegs:
770            self.serial +=1
771            self.uniqueID = 'M*%d' % self.serial
772            self.Segments.visualise(segment,
773                                    self.uniqueID,
774                                    self.canvas,
775                                    self.SCALE)
776       
777    def joinVerticesButton (self, parent):
778        self.joinVertices()
779       
780    def joinVertices (self):
781        """
782        add Segments to connect all vertices
783       
784        the parent attribute isn't used by this function.
785        need to userstand toolbarbutton.py to know how to
786        get rid of it.
787        """
788        if len(self.mesh.getUserVertices()) >= 3:
789            newsegs = self.mesh.joinVertices()
790            for segment in newsegs:
791                self.serial +=1
792                self.uniqueID = 'M*%d' % self.serial
793                self.Segments.visualise(segment,
794                                        self.uniqueID,
795                                        self.canvas,
796                                        self.SCALE)
797        else:
798            showerror('pMesh',
799                      'Three or more vetices are needed to be able to join vertices.')
800       
801    def windowMeshGen (self, parent):
802        """
803        The parent attribute isn't used by this function.
804        need to understand toolbarbutton.py to know how to
805        get rid of it.
806        """
807        # Put exceptions round things.
808        #catch failure in self.mesh.generateMesh
809        dialog = MeshGenDialog(self.canvas,
810                               self.MeshMinAngle,
811                               self.MeshMaxArea,
812                               self.MeshnumTriangles,
813                               self.MeshMaxAreaLast)
814       
815        if dialog.ValuesOk:
816            print dialog.minAngle
817            print dialog.maxArea
818           
819            self.clearSelections()
820            self.canvas.delete(ALL)
821            if dialog.goodMaxArea == True:
822                self.MeshMinAngle = dialog.minAngle
823                self.MeshMaxArea = dialog.maxArea
824                self.MeshMaxAreaLast = True
825               
826                self.mesh = self.MeshGenAreaAngle (dialog.minAngle,
827                                          dialog.maxArea,
828                                          self.mesh)
829            elif dialog.goodNumTriangles == True:
830                self.MeshMinAngle = dialog.minAngle
831                self.MeshnumTriangles  = dialog.numTriangles
832                self.MeshMaxAreaLast = False
833               
834                self.mesh = self.MeshGenAreaNumTriangles (dialog.minAngle,
835                                          dialog.numTriangles,
836                                          self.mesh)
837            else:
838                pass
839            print "userMeshChanged = False"
840            self.UserMeshChanged = False
841            self.visualiseMesh(self.mesh)
842            print "Mesh Generation finished"
843           
844    def MeshGenAreaAngle (self, minAngle, maxArea, mesh):
845        """
846        Generate a mesh, given a minAngle and max area
847        """
848        tempMesh = mesh
849        try:
850            tempMesh.generateMesh(mode = "pzq"+str(minAngle)
851                                  +"a"+str(maxArea)
852                                  +"a") #So areas for regions will be used
853        except AttributeError : # can't catch PyEval_RestoreThread
854            # This doesn't catch tempMesh.generateMesh failing
855            tempMesh = mesh
856        return tempMesh
857       
858
859    def MeshGenAreaNumTriangles (self, minAngle, numTriangles, mesh):
860        """
861        Generate a mesh, given a minAngle and rough # of triangles
862        """
863        #get big triangles
864        #calc area
865        #calc max triangle area
866        #
867        tempMesh = mesh
868        try:
869            tempMesh.generateMesh("pzq1")
870        except AttributeError : # can't catch PyEval_RestoreThread
871            # This doesn't catch tempMesh.generateMesh failing
872            pass
873        meshArea = 0
874        for triangle in tempMesh.getTriangulation():
875             meshArea += triangle.calcArea()
876        print "meshArea: ", meshArea
877
878        maxArea = meshArea/numTriangles
879
880       
881        return self.MeshGenAreaAngle (minAngle,
882                                      maxArea,
883                                      self.mesh)
884       
885    def mouseDown(self, event):
886        """
887        On a mouse down event, depending on the current state,
888        either add a vertex or a seg etc
889        """
890        self.curObject = None
891        self.lastx = self.startx = self.canvas.canvasx(event.x)
892        #The screen canvas has y 'flipped'.  -1* unflips it
893        self.lasty = self.starty = -1*self.canvas.canvasy(event.y)
894        print "----------------------"
895        self.mouseDownCurFunc( self.lastx,
896                               self.lasty,event) #!!! remove the event?
897                                                 # do last
898   
899    def rightMouseUp(self, event):
900        """
901        On a right mouse button up event select the nearest object.
902        """
903        found=False
904        if event.widget.find_withtag(CURRENT): # if there's a widget with a tag
905            [tag,string] = self.canvas.gettags(CURRENT) # get a list of them
906            print "tag",tag  #tags ('M*1008', 'current')
907            if tag[:2] == 'M*':   #!!! this can be removed when there are
908                #    only mesh objects on screen
909                #key, value = string.split(tag, '*')
910                objectID = tag
911                print "Found!! objectID:", objectID
912               
913                meshObjects = self.getAllUserMeshObjects()
914                # It may be a triangle, which is ignored
915                if meshObjects.hasKey(objectID):
916                    selMeshObject = meshObjects.getMeshObject(objectID)
917                    found = True
918                    print "Found! selMeshObject", selMeshObject
919                    #Only select one object at a time
920                    if self.selMeshObject:
921                        self.deselectMeshObject(self.selMeshObject, self.selMeshTag)
922                    self.selectMeshObject(selMeshObject,objectID)
923
924    def getAllUserMeshObjects(self):
925        return self.UserMesh
926       
927    def DeleteSelectedMeshObject(self, event):
928        """
929        if an object is selected, delete it.
930        """
931        if self.selMeshObject:
932            #an object is selected
933            #first deselect the vertex, for selecting a segment
934            if self.selVertex:
935                self.deselectVertex(self.selVertex, self.selVertexTag)
936            ObjectsToVisuallyDelete = self.mesh.deleteMeshObject (self.selMeshObject)
937            for drawOb in ObjectsToVisuallyDelete:
938                self.UserMesh.unvisualise(drawOb, self.canvas)
939               
940            self.selMeshObject = None
941            self.selMeshTag = None
942           
943    def selectMeshObject(self, meshObject, objectID):
944        """
945        selected a mesh object.
946        """
947        self.canvas.delete(objectID)
948        self.selMeshObject = meshObject
949        self.selMeshTag = objectID
950        meshObject.draw(self.canvas,objectID, scale =self.SCALE ,colour = SELECT_COLOR)
951   
952    def deselectMeshObject(self, meshObject, objectID):
953        """
954        deselected a mesh object.
955        """
956        self.canvas.delete(objectID)
957        self.selMeshObject = None
958        self.selMeshTag = None
959        if isinstance(meshObject, mesh.Segment):
960            meshObject.draw(self.canvas,objectID,
961                        scale =self.SCALE ,colour = SEG_COLOUR)
962        else:
963            meshObject.draw(self.canvas,objectID,
964                        scale =self.SCALE )
965           
966    def drag(self,x,y,event):
967        """
968        Hack function.  called when in select and left mouse goes down
969        """
970        pass
971   
972   
973    def drawEastingNorthingVertex(self,x,y,event):
974        """
975        draw a vertex object, plus add it to the mesh data structure
976
977        event isn't used
978        """
979        self.serial +=1
980        self.uniqueID = 'M*%d' % self.serial
981        #x_scaled =  self.SCALE*x
982        #y_scaled = -1*self.SCALE*y
983        #print "x y:", x,y
984        vert = self.Vertices.draw(x-self.mesh.geo_reference.get_xllcorner,
985                                  y-self.mesh.geo_reference.get_yllcorner,
986                                  self.mesh,
987                                  self.uniqueID,
988                                  self.SCALE,
989                                  self.canvas,
990                                  event) #FIXME why is event passed on.
991        self.UserMeshChanged = True
992        return vert
993   
994    def drawVertex(self,x,y,event):
995        """
996        draw a vertex object, plus add it to the mesh data structure
997
998        event isn't used
999        """
1000        self.serial +=1
1001        self.uniqueID = 'M*%d' % self.serial
1002        #x_scaled =  self.SCALE*x
1003        #y_scaled = -1*self.SCALE*y
1004        #print "x y:", x,y
1005        vert = self.Vertices.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event)
1006        self.UserMeshChanged = True
1007        return vert
1008     
1009    def drawHole(self,x,y,event):
1010        """
1011        draw a hole object, plus add it to the mesh data structure
1012
1013        event isn't used
1014        """
1015        self.serial +=1
1016        self.uniqueID = 'M*%d' % self.serial
1017        self.userMeshChanged = True
1018        hole = self.Holes.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event)
1019        return hole   
1020   
1021    def drawRegion(self,x,y,event):
1022        """
1023        draw a region object, plus add it to the mesh data structure
1024
1025        event isn't used
1026        """
1027        self.serial +=1
1028        self.uniqueID = 'M*%d' % self.serial
1029        region = self.Regions.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event)
1030        return region
1031   
1032    def selectSegmentPoint(self,x,y, event):
1033        """
1034        logic when selecting a vertex object to add a segment
1035        """
1036        found=False
1037        if event.widget.find_withtag(CURRENT): # if there's a widget with a tag
1038            [tag,string] = self.canvas.gettags(CURRENT) # get a list of them
1039            print "tag",tag  #tags ('M*1008', 'current')
1040            objectID = tag
1041            #print "Found!! objectID:", objectID
1042            if self.Vertices.hasKey(objectID): #isinstance(self.meshObjects[objectID],mesh.Vertex):
1043                vertex = self.Vertices.getMeshObject(objectID)
1044                found = True
1045                print "Found! vertex", vertex
1046           
1047        if found and self.selVertex == vertex:
1048            print "The selected vertex has already been selected"
1049            #The selected vertex has already been selected
1050            # therefore deselect it
1051            self.deselectVertex(self.selVertex, self.selVertexTag)
1052            found = False
1053                 
1054        if found: 
1055            #A vertex has been selected!
1056            if self.selVertex:
1057                if self.mesh.isUserSegmentNew(self.selVertex,vertex):
1058                    #vertex is the 2nd vertex
1059                    self.drawSegment(vertex,self.selVertex)
1060                    self.deselectVertex(self.selVertex, self.selVertexTag)
1061                    self.selectVertex(vertex,objectID)
1062            else:
1063                print "vertex is the 1st vertex" 
1064                #vertex is the 1st vertex
1065                self.selectVertex(vertex,objectID)
1066        else:
1067            print " There are no widgets.  This happen's too much"
1068                   
1069
1070    def selectVertex(self, vertex,objectID):
1071        """
1072        select a vertex object when adding a segment
1073        """
1074        self.canvas.delete(objectID)
1075        self.selVertex = vertex
1076        self.selVertexTag = objectID
1077        vertex.draw(self.canvas,objectID, scale =self.SCALE ,colour = VERT_SELECT_ADDING_SEG_COLOR)
1078   
1079    def deselectVertex(self, vertex,objectID):
1080        """
1081        deselect a vertex object when adding a segment
1082        """
1083        self.canvas.delete(objectID)
1084        self.selVertex = None
1085        self.selVertexTag = None
1086        vertex.draw(self.canvas,objectID,  scale =self.SCALE )
1087         
1088    def drawSegment(self,v1,v2):
1089        """
1090        Create a seg object, draw it and add it to the mesh data structure
1091        """
1092        self.serial +=1
1093        self.uniqueID = 'M*%d' % self.serial
1094        self.userMeshChanged = True
1095        seg = self.Segments.draw(v1,v2,self.mesh,self.uniqueID,self.SCALE,self.canvas,None)
1096        return seg
1097    def printGeoReference(self):
1098        try:
1099            print "geo reference", self.mesh.geo_reference
1100        except:
1101            print "no geo reference"
1102       
1103    def visualiseMesh(self,mesh):
1104        """
1105        visualise vertices, segments, triangulation, holes
1106        """
1107        if self.Visualise:
1108            for triangle in mesh.getTriangulation():
1109                self.serial +=1
1110                self.uniqueID = 'M*%d' % self.serial
1111                self.Triangles.visualise(triangle,
1112                                        self.uniqueID,
1113                                        self.canvas,
1114                                        self.SCALE)
1115
1116        if self.Visualise:
1117            Triangles = mesh.sets[mesh.setID[self.selSet]]
1118            for triangle in Triangles:
1119                triangle.draw(self.canvas,1,
1120                              scale = self.SCALE,
1121                              colour = SET_COLOUR)
1122
1123        for segment in mesh.getUserSegments():
1124            self.serial +=1
1125            self.uniqueID = 'M*%d' % self.serial
1126            self.Segments.visualise(segment,
1127                                    self.uniqueID,
1128                                    self.canvas,
1129                                    self.SCALE)
1130        for vertex in mesh.getUserVertices():
1131            self.serial +=1
1132            self.uniqueID = 'M*%d' % self.serial
1133            self.Vertices.visualise(vertex,
1134                                    self.uniqueID,
1135                                    self.canvas,
1136                                    self.SCALE)
1137           
1138        for hole in mesh.getHoles():
1139            self.serial +=1
1140            self.uniqueID = 'M*%d' % self.serial
1141            self.Holes.visualise(hole,
1142                                    self.uniqueID,
1143                                    self.canvas,
1144                                    self.SCALE)   
1145        for region in mesh.getRegions():
1146            self.serial +=1
1147            self.uniqueID = 'M*%d' % self.serial
1148            self.Regions.visualise(region,
1149                                    self.uniqueID,
1150                                    self.canvas,
1151                                    self.SCALE)
1152    def normalise4ObjMesh(self):
1153        if self.mesh:
1154            self.clearSelections()
1155            self.canvas.delete(ALL)
1156            self.mesh.normaliseMesh(400,-200,20)
1157            self.visualiseMesh(self.mesh)
1158            self.ResizeToFit()
1159            self.ResizeToFit()
1160           
1161    def normaliseMesh(self):
1162        if self.mesh:
1163            self.clearSelections()
1164            self.canvas.delete(ALL)
1165            self.mesh.normaliseMesh(1,0,1)
1166            self.visualiseMesh(self.mesh)
1167            self.ResizeToFit()
1168            self.ResizeToFit()
1169           
1170       
1171    def clearMesh(self):
1172        """Clear the current mesh object, and the canvas """
1173       
1174        self.clearSelections()
1175        self.canvas.delete(ALL)
1176        self.deleteMesh()
1177        self.initData()
1178        self.createMesh()
1179
1180    def exportObj(self):
1181        fileType = "obj"
1182        fileTypeDesc = "obj mesh"
1183       
1184        ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType),
1185                                                ("All Files", "*")])
1186        if ofile:
1187            addOn = "." + fileType
1188            jumpback = - len(addOn)
1189            if ofile[jumpback:] != addOn: 
1190                ofile = ofile + addOn
1191            try:
1192                self.mesh.exportASCIIobj(ofile)
1193            except IOError:
1194                showerror('Export ASCII file',
1195                                   'Can not write to file.')
1196            except RuntimeError:
1197                showerror('Export ASCII file',
1198                                   'No triangulation to export.')
1199
1200   
1201
1202    def ImportUngenerate(self):
1203        ofile = tkFileDialog.askopenfilename(initialdir=self.currentPath,
1204                                             filetypes=[ ("ungenerated polygon information", "txt"),
1205                                           ("All Files", "*")])
1206        if ofile == "":
1207            # The user cancelled the loading action
1208            return
1209       
1210        try:
1211            self.clearSelections()
1212            self.canvas.delete(ALL)
1213            dict = mesh.importUngenerateFile(ofile)
1214            self.mesh.addVertsSegs(dict)
1215           
1216        except SyntaxError: 
1217            #this is assuming that the SyntaxError is thrown in
1218            #loadxyafile
1219            showerror('File error',
1220                      ofile + ' is not in the correct format.')
1221        except IOError: 
1222            #!!! this error type can not be thrown?
1223            showerror('File error',
1224                      'file ' + ofile + ' could not be found.')
1225        except RuntimeError: 
1226            showerror('File error',
1227                  'file ' + ofile + ' has an unknown file type.')
1228   
1229        self.visualiseMesh(self.mesh)
1230        self.ResizeToFit()
1231       
1232    def exportASCIIsegmentoutlinefile(self):
1233       
1234        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
1235                                               filetypes=[("mesh", "*.tsh *.msh"),
1236                                             ("All Files", "*")])
1237           
1238        if ofile:
1239            # .tsh is the default file format
1240            if (ofile[-4:] == ".tsh" or ofile[-4:] == ".msh"): 
1241                self.currentFilePathName = ofile
1242            else:
1243                self.currentFilePathName = ofile + ".tsh"
1244               
1245            try:
1246                self.mesh.exportASCIIsegmentoutlinefile(ofile)
1247            except IOError: #FIXME should this function be throwing any errors?
1248                showerror('Export ASCII file',
1249                                   'Can not write to file.')
1250
1251    def exportPointsFile(self):
1252        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
1253                                         filetypes=[("point files",
1254                                                     "*.xya *.pts"),
1255                                                ("All Files", "*")])
1256        if ofile:
1257            # .xya is the default file format
1258            if (ofile[-4:] == ".xya" or ofile[-4:] == ".pts"): 
1259                self.currentFilePathName = ofile
1260            else:
1261                self.currentFilePathName = ofile + ".xya"
1262               
1263            try:
1264                self.mesh.exportPointsFile(ofile)
1265            except IOError:
1266                showerror('Export ASCII file',
1267                                   'Can not write to file.')
1268                   
1269    def importFile(self):
1270        """
1271        import mesh data from a variety of formats (currently 2!)
1272        """
1273        print "self.currentPath",self.currentPath
1274        ofile = tkFileDialog.askopenfilename(initialdir=self.currentPath,
1275                                             filetypes=[ ("text Mesh", "*.tsh *.msh"),
1276                                                         ("points", "*.xya *.pts"),
1277                                           ("All Files", "*")])
1278        if ofile == "":
1279            # The user cancelled the loading action
1280            return
1281       
1282        try:
1283            newmesh = mesh.importMeshFromFile(ofile)
1284            self.currentPath, dummy = os.path.split(ofile)
1285            #print "be good self.currentPath",self.currentPath
1286            self.currentFilePathName = ofile
1287            self.clearMesh()
1288            self.mesh = newmesh
1289
1290            #FIXME - to speed things up, don't visualise the mesh
1291            # use ResizeToFitWrapper
1292            self.visualiseMesh(self.mesh)
1293            self.ResizeToFit()
1294       
1295        except IOError: 
1296            #!!! this error type can not be thrown?
1297            showerror('File error',
1298                      'file ' + ofile + ' could not be loaded.')
1299
1300        except RuntimeError: 
1301            showerror('File error',
1302                  'file ' + ofile + ' has an unknown file type.')
1303        # Could not get the file name to showup in the title
1304        #appname =  ofile + " - " + APPLICATION_NAME
1305        #print "appname",appname
1306       
1307        except load_mesh.loadASCII.TitleAmountError: 
1308            showerror('File error',
1309                  'file ' + ofile + ' has a bad title line (first line).')
1310
1311   
1312    def ResizeToFitWrapper(self, Parent):
1313        """
1314        The parent attribute isn't used by this function.
1315        need to understand toolbarbutton.py to know how to
1316        get rid of it.
1317        """
1318        self.ResizeToFit()
1319       
1320    def ResizeToFit(self):
1321        """Visualise the mesh so it fits in the window"""
1322        if self.mesh.getUserVertices() == []:
1323            return #There are no vertices!
1324        # Resize the window
1325        self.scrolledcanvas.resizescrollregion()
1326        # I need this so the xview values are correct
1327        self.scrolledcanvas.update()
1328       
1329        xtop, xbottom = self.scrolledcanvas.xview()
1330        ytop, ybottom = self.scrolledcanvas.yview()
1331        xdiff = xbottom-xtop
1332        ydiff = ybottom-ytop
1333        if xdiff == 1 and xdiff == 1:
1334            #The mesh might be too small.
1335            #Make it too large, then resize
1336            #!!! Recursive hack.  Should be a better way
1337            fraction = 50
1338            self.SCALE *= fraction
1339            self.scrolledcanvas.scale(ALL, 0, 0, fraction, fraction)
1340            self.ResizeToFit()
1341        else:
1342            # without 0.99 some of the mesh may be off screen
1343            fraction = 0.99*min(xdiff,ydiff) 
1344            self.selectZoom(fraction)
1345           
1346    def saveDrawing(self):
1347        """
1348        Save the current drawing
1349        """
1350        #print "dsg!!! self.currentFilePathName ",self.currentFilePathName
1351        if (self.currentFilePathName[-4:] != ".tsh" or
1352            self.currentFilePathName[-4:] != ".msh"):
1353            # force user to choose a name
1354            self.saveAsDrawing()
1355        else:
1356            self.exportASCIItriangulationfile(self.currentFilePathName)
1357
1358    def saveAsDrawing(self):
1359        """
1360        Save the current drawing, prompting for a file name
1361        """
1362        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
1363                                               filetypes=[("mesh", "*.tsh *.msh"),
1364                                             ("All Files", "*")])
1365           
1366        if ofile:
1367            # .tsh is the default file format
1368            if (ofile[-4:] == ".tsh" or ofile[-4:] == ".msh"): 
1369                self.currentFilePathName = ofile
1370            else:
1371                self.currentFilePathName = ofile + ".tsh"
1372            self.exportASCIItriangulationfile(self.currentFilePathName)
1373
1374    def exportASCIItriangulationfile(self,currentFilePathName):
1375        """
1376        Have a warning prompt when saving a mesh where the generated mesh is
1377        different from the user mesh - eg boundary tags that aren't carried
1378        thru. Warning ~"Mesh not generated after changes.  Generate mesh?  "
1379        - cancel, don't gen, don't save.  Yes - generate mesh, go to save
1380        screen.  No - goto save screen.  To implement this need to know when
1381        the user has done a change, and the mesh hasn't been generated.  If
1382        there is no generated mesh do not prompt.
1383        """
1384        #print "self.UserMeshChanged",self.UserMeshChanged
1385        #print "self.mesh.isTriangulation()",self.mesh.isTriangulation()
1386        if (self.UserMeshChanged) and self.mesh.isTriangulation():
1387           
1388            m = _show("Warning",
1389                                   "A triangulation has not been generated, after mesh changes.  Generate triangulation before saving?",
1390                                   icon=QUESTION, 
1391                                   type=YESNOCANCEL)
1392            if m == "no":
1393                self.mesh.export_mesh_file(currentFilePathName)
1394                self.UserMeshChanged = False
1395            elif m == "cancel":
1396                pass
1397            elif m == "yes":
1398                self.windowMeshGen(None)
1399                self.mesh.export_mesh_file(currentFilePathName)
1400        else:
1401            self.mesh.export_mesh_file(currentFilePathName)
1402            self.UserMeshChanged = False
1403           
1404    def initData(self):
1405        """
1406        Initialise various lists and flags
1407        """
1408        self.serial      = 1000
1409        self.currentFilePathName = 'untitled'
1410
1411        # these are attributes I've added
1412        self.SCALE       = 1
1413        self.selVertex   = None     #The last vertex selected, in draw seg mode
1414        self.selVertexTag   = None     # The selected vertex drawn object tag
1415        self.mesh        = None
1416        self.MeshMinAngle = 20
1417        self.MeshMaxArea = 200
1418        self.MeshnumTriangles = 20
1419        self.MeshMaxAreaLast = False
1420        self.selMeshObject = None  # The mesh object selected in the current mode
1421        self.selMeshTag = None
1422        mesh.Segment.set_default_tag("")
1423        self.UserMeshChanged = False
1424        self.meshLastAlpha = None
1425       
1426        self.Visualise = True #Is the mesh shown or not?
1427   
1428    def ipostscript(self):
1429        """
1430        Print the canvas as a postscript file
1431        """
1432        ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath,
1433                                               filetypes=[("postscript", "ps"),
1434                                                          ("All Files", "*")]) 
1435        if ofile:
1436            if ofile[-3:] != ".ps": 
1437                ofile = ofile + ".ps"
1438            postscript = self.canvas.postscript()
1439            fd = open(ofile, 'w')
1440            fd.write(postscript)
1441            fd.close()
1442       
1443    def close(self):
1444        self.quit()
1445       
1446    def createInterface(self):
1447        """
1448        Call all functions that create the GUI interface
1449        """
1450        self.initData()
1451        self.createMesh()
1452        AppShell.AppShell.createInterface(self)
1453        self.createButtons()
1454        self.createMenus()
1455        self.createBase()
1456        self.createTools()
1457        self.createZooms()
1458        self.createEdits()
1459        self.createSetTools()
1460        self.createSetIcons()
1461        self.createVisualiseIcons()
1462        #self.addCylinders() # !!!DSG start pmesh with a triangle
1463        self.selectFunc('pointer')
1464        self.currentPath = os.getcwd() 
1465
1466    def loadtestmesh(self,ofile):
1467        """
1468        debugging script to test loading a file
1469        """
1470        fd = open(ofile)
1471        a = mesh.Vertex (-10.0, 0.0)
1472        d = mesh.Vertex (0.0, 4.0)
1473        f = mesh.Vertex (4.0,0.0)
1474        g = mesh.Vertex (-5.0,5.0)
1475       
1476        s1 = mesh.Segment(a,d)
1477        s2 = mesh.Segment(d,f)
1478        s3 = mesh.Segment(a,f)
1479
1480        r1 = mesh.Region(0.3, 0.3)
1481       
1482        m = mesh.Mesh(userVertices=[a,d,f,g], userSegments=[s1,s2,s3], regions=[r1] )
1483       
1484        fd.close()
1485        print 'returning m'
1486        return oadtestmesh(ofile)
1487         
1488class  AddVertexDialog(Dialog):
1489    """
1490    Dialog box for adding a vertex by entering co-ordindates
1491    """
1492    def body(self, master):
1493        """
1494        GUI description
1495        """
1496        self.title("Add New Vertex")
1497       
1498        Label(master, text='X position:').grid(row=0, sticky=W)
1499        Label(master, text='Y position:').grid(row=1, sticky=W)
1500
1501        self.xstr   = Entry(master, width = 16, name ="entry")
1502        self.ystr  = Entry(master, width = 16)
1503       
1504        self.xstr.grid(row=0, column=1, sticky=W)
1505        self.ystr.grid(row=1, column=1, sticky=W)
1506        self.xstr.focus_force()
1507        self.= 0
1508        self.= 0
1509        self.xyValuesOk = False
1510 
1511
1512    def apply(self):
1513        """
1514        check entered values
1515        """
1516        try:
1517            self.x = float(self.xstr.get())
1518            self.y = float(self.ystr.get())
1519            self.xyValuesOk = True
1520           
1521        except ValueError:
1522            showerror('Bad Vertex values',
1523                                   'X Y values are not numbers.')
1524       
1525
1526class  AutoSegmentDialog(Dialog):
1527    """
1528    Dialog box for adding segments
1529    """
1530    def __init__(self, parent, alpha):
1531        self.alpha = alpha
1532        Dialog.__init__(self, parent)
1533       
1534    def body(self, master):
1535        """
1536        GUI description
1537        """
1538        self.title("Automatically Add Segments")
1539
1540        self.use_optimum = IntVar()
1541        self.use_optimum.set(AUTO) # should initialise the radio buttons.
1542                                   #  It doesn't
1543                                   
1544        #self.use_optimum.set(NO_SELECTION)
1545        self.ck = Radiobutton(master, value = AUTO, variable=self.use_optimum) 
1546        self.ck.grid(row=1, column=0)
1547        Label(master, text='Use optimum alpha').grid(row=1, column=1, sticky=W)
1548
1549        self.ck2 = Radiobutton(master, value = SET_ALPHA,
1550                               variable=self.use_optimum) 
1551        self.ck2.grid(row=2, column=0)
1552       
1553        Label(master, text='alpha:').grid(row=2, column=1, sticky=W)
1554        if (self.alpha):
1555            alphaVar = StringVar()
1556            alphaVar.set(self.alpha)
1557            self.alpha_str  = Entry(master,
1558                                     textvariable = alphaVar,
1559                                     width = 16, name ="entry") 
1560        else: 
1561            self.alpha_str = Entry(master, width = 16, name ="entry")
1562       
1563        self.alpha_str.grid(row=2, column=3, sticky=W)
1564
1565        #boundary type buttons
1566        self.raw_boundary = IntVar()
1567        self.remove_holes = IntVar()
1568        self.smooth_indents = IntVar()
1569        self.expand_pinch = IntVar()
1570        self.ck3 = Checkbutton(master, state=NORMAL,
1571                               variable=self.raw_boundary) 
1572        self.ck3.grid(row=3, column=0)
1573        Label(master, text='Raw boundary').grid(row=3, column=1, sticky=W)
1574        #
1575        self.ck4 = Checkbutton(master, state=NORMAL,
1576                               variable=self.remove_holes) 
1577        self.ck4.grid(row=4, column=0)
1578        Label(master, text='Remove small holes').grid(row=4,column=1, sticky=W)
1579        #
1580        self.ck5 = Checkbutton(master,state=NORMAL,
1581                               variable=self.smooth_indents) 
1582        self.ck5.grid(row=5, column=0)
1583        Label(master,
1584              text='Remove sharp indents').grid(row=5, column=1, sticky=W)
1585        #
1586        self.ck6 = Checkbutton(master,state=NORMAL,
1587                               variable=self.expand_pinch) 
1588        self.ck6.grid(row=6, column=0)
1589        Label(master,
1590              text='Remove pinch off').grid(row=6, column=1,  sticky=W)
1591
1592       
1593        self.alpha  = 0
1594        self.alphaValueOk = False
1595       
1596
1597    def apply(self):
1598        """
1599        check entered values
1600        """
1601        try:
1602            self.alpha = float(self.alpha_str.get())
1603            self.alphaValueOk = True
1604           
1605        except ValueError:
1606            pass
1607            #showerror('Bad Alpha value',
1608            #                       'Alpha is negative.')
1609
1610
1611class  AutoSegmentFilterDialog(Dialog):
1612    """
1613    Dialog box for adding segments
1614    """
1615    def __init__(self, parent):
1616        Dialog.__init__(self, parent)
1617       
1618    def body(self, master):
1619        """
1620        GUI description
1621        """
1622        self.title("Automatically Add Segments")
1623
1624        self.use_optimum = IntVar()
1625        self.use_optimum.set(AUTO) # should initialise the radio buttons.
1626                                   #  It doesn't
1627        self.boundary_type = IntVar()
1628                     
1629        #boundary type buttons
1630        self.raw_boundary = IntVar()
1631        self.remove_holes = IntVar()
1632        self.smooth_indents = IntVar()
1633        self.expand_pinch = IntVar()
1634       
1635        self.ck3 = Checkbutton(master, state=NORMAL,
1636                               variable=self.raw_boundary) 
1637        self.ck3.grid(row=3, column=0)
1638        Label(master, text='Raw boundary').grid(row=3, column=1, sticky=W)
1639        #
1640        self.ck4 = Checkbutton(master, state=NORMAL,
1641                               variable=self.remove_holes) 
1642        self.ck4.grid(row=4, column=0)
1643        Label(master, text='Remove small holes').grid(row=4,column=1, sticky=W)
1644        #
1645        self.ck5 = Checkbutton(master,state=NORMAL,
1646                               variable=self.smooth_indents) 
1647        self.ck5.grid(row=5, column=0)
1648        Label(master,
1649              text='Remove sharp indents').grid(row=5, column=1, sticky=W)
1650        #
1651        self.ck6 = Checkbutton(master,state=NORMAL,
1652                               variable=self.expand_pinch) 
1653        self.ck6.grid(row=6, column=0)
1654        Label(master,
1655              text='Remove pinch off').grid(row=6, column=1,  sticky=W)
1656       
1657       
1658
1659
1660class  MeshGenDialog(Dialog):
1661    """
1662    Dialog box for generating a mesh
1663    """
1664    # initial values, hard coded.
1665    # should be values associated with the current mesh
1666    lastMinAngle = 20
1667    lastMaxArea  = 100
1668
1669
1670    def __init__(self,
1671                 parent,
1672                 minAngle,
1673                 maxArea,
1674                 numTriangles,
1675                 MeshMaxAreaLast):
1676        self.minAngle = minAngle
1677        self.maxArea = maxArea
1678        self.numTriangles = numTriangles
1679        self.MeshMaxAreaLast = MeshMaxAreaLast
1680
1681        Dialog.__init__(self, parent)
1682
1683       
1684    def body(self, master):
1685        """
1686        GUI description
1687        """
1688        self.title("Generate Mesh")
1689       
1690        Label(master,
1691              text='Minimum Angle(0 - 40):').grid(row=0, sticky=W)
1692        Label(master,
1693              text='Angles>33 may not converge').grid(row=1, sticky=W)
1694        Label(master, text='Maximum Area:').grid(row=2, sticky=W)
1695        Label(master, text='OR # of triangles:').grid(row=3, sticky=W)
1696
1697
1698        minAngleVar = StringVar()
1699        minAngleVar.set(self.minAngle)
1700        self.minAnglestr   = Entry(master,
1701                                   width = 16,
1702                                   textvariable = minAngleVar,
1703                                   takefocus=1)
1704        if (self.MeshMaxAreaLast):
1705            maxAreaVar = StringVar()
1706            maxAreaVar.set(self.maxArea)
1707            self.maxAreastr  = Entry(master,
1708                                     textvariable = maxAreaVar,
1709                                     width = 16)   
1710            self.numTrianglesstr  = Entry(master,
1711                                          width = 16) 
1712        else: 
1713            self.maxAreastr  = Entry(master,
1714                                     width = 16)
1715            self.maxAreastr.focus_force()
1716            numTrianglesVar = StringVar()
1717            numTrianglesVar.set(self.numTriangles)   
1718            self.numTrianglesstr  = Entry(master,
1719                                          textvariable = numTrianglesVar,
1720                                          width = 16) 
1721
1722
1723        self.minAnglestr.grid(row=0, column=1, sticky=W)
1724        self.maxAreastr.grid(row=2, column=1, sticky=W)
1725        self.numTrianglesstr.grid(row=3, column=1, sticky=W)
1726
1727        self.numTriangles = 0
1728        self.minAngle  = 0
1729        self.maxArea  = 0
1730        self.ValuesOk = False
1731
1732
1733    def apply(self):
1734        """
1735        check entered values
1736        """
1737        self.goodMaxArea = self.goodNumTriangles = True
1738        self.ValuesOk = True
1739        try:
1740            self.minAngle = float(self.minAnglestr.get())
1741            MeshGenDialog.lastMinAngle =self.minAngle
1742        except ValueError:
1743            self.ValuesOk = False
1744            showerror('Bad mesh generation values',
1745                                   ' Values are not numbers.')
1746       
1747        try:   
1748            self.maxArea = float(self.maxAreastr.get())
1749            MeshGenDialog.lastMaxArea =self.maxArea
1750        except ValueError:
1751            self.goodMaxArea = False
1752         
1753        try:   
1754            self.numTriangles = int(self.numTrianglesstr.get())
1755            MeshGenDialog.lastNumTriangles =self.numTriangles
1756        except ValueError:
1757            self.goodNumTriangles= False
1758
1759        if self.goodMaxArea == False and self.goodNumTriangles == False:
1760            self.ValuesOk = False
1761            showerror('Bad mesh generation values',
1762                      'Values are not numbers.')
1763
1764        if self.goodMaxArea == True and self.goodNumTriangles == True:
1765            self.ValuesOk = False
1766            showerror('Bad mesh generation values',
1767                      'Give a maximum area OR number of triangles, not both.')
1768
1769        try: 
1770            # value checking
1771            if self.minAngle <0.0 or self.minAngle >40.0:
1772                raise IOError
1773            if self.goodMaxArea == True and self.maxArea <0.0:
1774                raise IOError
1775            if self.goodNumTriangles == True and self.numTriangles <=0:
1776                raise IOError
1777           
1778        except IOError:
1779            self.ValuesOk = False
1780            showerror('Bad mesh generation values',
1781                                   'Values are out of range.')
1782class  GeneralThresholdDialog(Dialog):
1783    """
1784    Dialog box for thresholding a set by entering minimum
1785    and maximum values
1786    """
1787    def __init__(self,
1788                 parent,
1789                 attribute_titles,
1790                 function_description):
1791        self.attribute_titles=attribute_titles
1792        self.function_description=function_description
1793
1794        Dialog.__init__(self, parent)
1795
1796
1797    def body(self, master):
1798        """
1799        GUI description
1800        """
1801        self.title("Threshold selected set")
1802        blurb1 = 'Threshold selected set between minimum'
1803        blurb2 = 'and maximum ' + self.function_description
1804
1805        Label(master,text=blurb1).grid(row=0, sticky=W)
1806        Label(master,text=blurb2).grid(row=1, sticky=W)
1807
1808        Label(master, text='minimum attribute:').grid(row=2, sticky=W)
1809        Label(master, text='maximum attribute:').grid(row=3, sticky=W)
1810        Label(master, text='attribute name').grid(row=4, sticky=W)
1811
1812
1813        nameVar = StringVar()
1814        nameVar.set('elevation')
1815
1816        self.minstr = Entry(master, width = 16, name ="entry")
1817        self.maxstr = Entry(master, width = 16)
1818        self.attstr = Entry(master, width = 16,textvariable = nameVar)
1819       
1820        self.minstr.grid(row=2, column=1, sticky=W)
1821        self.maxstr.grid(row=3, column=1, sticky=W)
1822        self.attstr.grid(row=4, column=1, sticky=W)
1823        self.minstr.focus_force()
1824        self.min  = 0
1825        self.max  = 0
1826        self.attribute_name = 'elevation'
1827        self.minmaxValuesOk = False
1828       
1829    def apply(self):
1830        self.minmaxValuesOk = True
1831        try:
1832            self.min = float(self.minstr.get())
1833            self.max = float(self.maxstr.get())
1834        except ValueError:
1835            self.minmaxValuesOk = False
1836            showerror('Bad mesh generation values',
1837                                   ' Values are not numbers.')
1838        try:
1839            self.attribute_titles.index(self.attstr.get())#dodgey.
1840            self.attribute_name = self.attstr.get()
1841        except ValueError:
1842            self.attribute_name = None
1843            showerror('Bad attribute name',
1844                                   'Using h = 1')
1845
1846class  ThresholdDialog(Dialog):
1847    """
1848    Dialog box for thresholding a set by entering minimum
1849    and maximum values
1850    """
1851    def __init__(self,
1852                 parent,
1853                 attribute_titles):
1854        self.attribute_titles=attribute_titles
1855        Dialog.__init__(self, parent)
1856
1857
1858    def body(self, master):
1859        """
1860        GUI description
1861        """
1862        self.title("Threshold selected set")
1863       
1864        Label(master, text='minimum attribute:').grid(row=0, sticky=W)
1865        Label(master, text='maximum attribute:').grid(row=1, sticky=W)
1866        Label(master, text='attribute name').grid(row=2, sticky=W)
1867
1868
1869        nameVar = StringVar()
1870        nameVar.set('elevation')
1871
1872        self.minstr   = Entry(master, width = 16, name ="entry")
1873        self.maxstr   = Entry(master, width = 16)
1874        self.attstr   = Entry(master, width = 16,textvariable = nameVar)
1875       
1876        self.minstr.grid(row=0, column=1, sticky=W)
1877        self.maxstr.grid(row=1, column=1, sticky=W)
1878        self.attstr.grid(row=2, column=1, sticky=W)
1879        self.minstr.focus_force()
1880        self.min  = 0
1881        self.max  = 0
1882        self.attribute_name = 'elevation'
1883        self.minmaxValuesOk = False
1884       
1885    def apply(self):
1886        self.minmaxValuesOk = True
1887        try:
1888            self.min = float(self.minstr.get())
1889            self.max = float(self.maxstr.get())
1890        except ValueError:
1891            self.minmaxValuesOk = False
1892            showerror('Bad mesh generation values',
1893                                   ' Values are not numbers.')
1894        try:
1895            self.attribute_titles.index(self.attstr.get())#dodgey.
1896            self.attribute_name = self.attstr.get()
1897        except ValueError:
1898            self.minmaxValuesOk = False
1899            showerror('Bad attribute name',
1900                                   ' Attribute not in mesh.')
1901
1902
1903class  Courant_ThresholdDialog(Dialog):
1904    """
1905    Dialog box for thresholding a set by entering minimum
1906    and maximum values
1907    """
1908    def __init__(self,
1909                 parent,
1910                 attribute_titles):
1911        self.attribute_titles=attribute_titles
1912        Dialog.__init__(self, parent)
1913
1914
1915    def body(self, master):
1916        """
1917        GUI description
1918        """
1919        self.title("Courant_Threshold selected set")
1920       
1921        Label(master, text='minimum attribute:').grid(row=0, sticky=W)
1922        Label(master, text='maximum attribute:').grid(row=1, sticky=W)
1923        Label(master, text='attribute name').grid(row=2, sticky=W)
1924
1925
1926        nameVar = StringVar()
1927        nameVar.set('elevation')
1928
1929        self.minstr   = Entry(master, width = 16, name ="entry")
1930        self.maxstr   = Entry(master, width = 16)
1931        self.attstr   = Entry(master, width = 16,textvariable = nameVar)
1932       
1933        self.minstr.grid(row=0, column=1, sticky=W)
1934        self.maxstr.grid(row=1, column=1, sticky=W)
1935        self.attstr.grid(row=2, column=1, sticky=W)
1936        self.minstr.focus_force()
1937        self.min  = 0
1938        self.max  = 0
1939        self.attribute_name = 'elevation'
1940        self.minmaxValuesOk = False
1941       
1942    def apply(self):
1943        self.minmaxValuesOk = True
1944        try:
1945            self.min = float(self.minstr.get())
1946            self.max = float(self.maxstr.get())
1947        except ValueError:
1948            self.minmaxValuesOk = False
1949            showerror('Bad mesh generation values',
1950                                   ' Values are not numbers.')
1951        try:
1952            self.attribute_titles.index(self.attstr.get())#dodgey.
1953            self.attribute_name = self.attstr.get()
1954        except ValueError:
1955            self.minmaxValuesOk = False
1956            showerror('Bad attribute name',
1957                                   ' Attribute not in mesh.')
1958
1959
1960class SmoothDialog(Dialog):
1961    """
1962    Dialog box for setting the number of triangles
1963    used to make up dilation or erosion
1964    """
1965    def body(self, master):
1966        """
1967        GUI description
1968        """
1969        self.title("Enter the minimum radius to remove")
1970       
1971        Label(master, text='radius:').grid(row=0, sticky=W)
1972
1973        self.min_radius = Entry(master, width = 16, name ="entry")
1974       
1975        self.min_radius.grid(row=0, column=1, sticky=W)
1976        self.min_radius.focus_force()
1977        self.min = 2.
1978        self.valueOK = False
1979       
1980    def apply(self):
1981        self.valueOK = True
1982        try:
1983            self.min = float(self.min_radius.get())
1984            self.min_radius = self.min
1985        except ValueError:
1986            self.valueOK = False
1987            showerror('Bad Number',
1988                                   ' Value not a number')
1989
1990class  setStructureNumberDialog(Dialog):
1991    """
1992    Dialog box for setting the number of triangles
1993    used to make up dilation or erosion
1994    """
1995    def body(self, master):
1996        """
1997        GUI description
1998        """
1999        self.title("Set number of elements effected by morphing sets")
2000       
2001        Label(master, text='number:').grid(row=0, sticky=W)
2002
2003        self.number = Entry(master, width = 16, name ="entry")
2004       
2005        self.number.grid(row=0, column=1, sticky=W)
2006        self.number.focus_force()
2007        self.number = 0
2008        self.numberOk = False
2009       
2010    def apply(self):
2011        self.numberOk = True
2012        try:
2013            self.number = int(self.number.get())
2014        except ValueError:
2015            self.numberOK = False
2016            showerror('Bad mesh generation values',
2017                                   ' Values are not numbers.')
2018
2019
2020     
2021if __name__ == '__main__':
2022    draw = Draw()
2023    draw.run()
2024    #profile.run('draw.run()', 'pmeshprof')   
2025
Note: See TracBrowser for help on using the repository browser.