~ubuntu-branches/ubuntu/gutsy/blender/gutsy-security

« back to all changes in this revision

Viewing changes to release/scripts/mesh_unfolder.py

  • Committer: Bazaar Package Importer
  • Author(s): Florian Ernst
  • Date: 2007-05-17 11:47:59 UTC
  • mfrom: (1.2.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20070517114759-yp4ybrnhp2u7pk66
Tags: 2.44-1
* New upstream release.
* Drop debian/patches/01_64bits_stupidity, not needed anymore: as of this
  version blender is 64 bits safe again. Adjust README.Debian accordingly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!BPY
 
2
"""
 
3
Name: 'Unfold'
 
4
Blender: 243
 
5
Group: 'Mesh'
 
6
Tip: 'Unfold meshes to create nets'
 
7
Version:  v2.2.4
 
8
Author: Matthew Chadwick
 
9
"""
 
10
import Blender
 
11
from Blender import *
 
12
from Blender.Mathutils import *
 
13
try:
 
14
        import sys
 
15
        import traceback
 
16
        import math
 
17
        import re
 
18
        from math import *
 
19
        import sys
 
20
        import random
 
21
        from decimal import *
 
22
        import xml.sax, xml.sax.handler, xml.sax.saxutils
 
23
 
 
24
except:
 
25
        print "One of the Python modules required can't be found."
 
26
        print sys.exc_info()[1]
 
27
        traceback.print_exc(file=sys.stdout)
 
28
        
 
29
__author__ = 'Matthew Chadwick'
 
30
__version__ = '2.2.4 24032007'
 
31
__url__ = ["http://celeriac.net/unfolder/", "blender", "blenderartist"]
 
32
__email__ = ["post at cele[remove this text]riac.net", "scripts"]
 
33
__bpydoc__ = """\
 
34
 
 
35
Mesh Unfolder
 
36
 
 
37
Unfolds the selected mesh onto a plane to form a net
 
38
 
 
39
Not all meshes can be unfolded
 
40
 
 
41
Meshes must be free of holes, 
 
42
isolated edges (not part of a face), twisted quads and other rubbish.
 
43
Nice clean triangulated meshes unfold best
 
44
 
 
45
This program is free software; you can distribute it and/or modify it under the terms
 
46
of the GNU General Public License as published by the Free Software Foundation; version 2
 
47
or later, currently at http://www.gnu.org/copyleft/gpl.html
 
48
 
 
49
The idea came while I was riding a bike.
 
50
 
 
51
"""     
 
52
 
 
53
# Face lookup
 
54
class FacesAndEdges:
 
55
        def __init__(self, mesh):
 
56
                self.nfaces = 0
 
57
                # straight from the documentation
 
58
                self.edgeFaces = dict([(edge.key, []) for edge in mesh.edges])
 
59
                for face in mesh.faces:
 
60
                        face.sel = False
 
61
                        for key in face.edge_keys:
 
62
                                        self.edgeFaces[key].append(face)
 
63
        def findTakenAdjacentFace(self, bface, edge):
 
64
                return self.findAdjacentFace(bface, edge)
 
65
        # find the first untaken (non-selected) adjacent face in the list of adjacent faces for the given edge
 
66
        def findAdjacentFace(self, bface, edge):
 
67
                faces = self.edgeFaces[edge.key()]
 
68
                for i in xrange(len(faces)):
 
69
                        if faces[i] == bface:
 
70
                                j = (i+1) % len(faces)
 
71
                                while(faces[j]!=bface):
 
72
                                        if faces[j].sel == False:
 
73
                                                return faces[j]
 
74
                                        j = (j+1) % len(faces)
 
75
                return None
 
76
        def returnFace(self, face):
 
77
                face.sel = False
 
78
                self.nfaces-=1
 
79
        def facesTaken(self):
 
80
                return self.nfaces
 
81
        def takeAdjacentFace(self, bface, edge):
 
82
                if (edge==None):
 
83
                        return None
 
84
                face = self.findAdjacentFace(bface, edge)
 
85
                if(face!=None):
 
86
                        face.sel = True
 
87
                        self.nfaces+=1
 
88
                        return face
 
89
        def takeFace(self, bface):
 
90
                if(bface!=None):
 
91
                        bface.sel= True
 
92
                        self.nfaces+=1
 
93
        
 
94
        
 
95
class IntersectionResult:
 
96
        def __init__(self, rn, rd, v=None):
 
97
                self.v = v
 
98
                self.rd = rd
 
99
                self.rn = rn
 
100
        def intersected(self):
 
101
                return not(not(self.v))
 
102
        def isParallel(self):
 
103
                return (self.rd==0)
 
104
        def isColinear(self):
 
105
                return (self.rn==0)
 
106
        def intersection(self):
 
107
                return self.v
 
108
        
 
109
# represents a line segment between two points [p1, p2]. the points are [x,y]
 
110
class LineSegment:
 
111
        def __init__(self, p):
 
112
                self.p = p
 
113
        def intersects(self, s):
 
114
                rn = ((self.p[0].y-s.p[0].y)*(s.p[1].x-s.p[0].x)-(self.p[0].x-s.p[0].x)*(s.p[1].y-s.p[0].y)) 
 
115
                rd = ((self.p[1].x-self.p[0].x)*(s.p[1].y-s.p[0].y)-(self.p[1].y-self.p[0].y)*(s.p[1].x-s.p[0].x))
 
116
                # need an epsilon closeTo() here
 
117
                if(rd<0.0000001 or rn==0.0):
 
118
                        return IntersectionResult(rn,rd)
 
119
                r = rn/rd
 
120
                s = ((self.p[0].y-s.p[0].y)*(self.p[1].x-self.p[0].x)-(self.p[0].x-s.p[0].x)*(self.p[1].y-self.p[0].y)) / rd
 
121
                i = (0.0<=r and r<=1.0 and 0.0<=s and s<=1.0)
 
122
                if not(i):
 
123
                        return None
 
124
                ix = self.p[0].x + r*(self.p[1].x - self.p[0].x)
 
125
                iy = self.p[0].y + r*(self.p[1].y - self.p[0].y)
 
126
                t = 0.0001
 
127
                if ( abs(ix-self.p[0].x)>t and abs(iy-self.p[0].x)>t and abs(ix-self.p[1].x)>t and abs(iy-self.p[1].y)>t ):
 
128
                        return IntersectionResult( rn, rd,Vector([ix,iy,0.0]))
 
129
                else:
 
130
                        return None
 
131
                
 
132
class LineSegments:
 
133
        def __init__(self, face):
 
134
                self.face = face
 
135
        def segmentAt(self, i):
 
136
                if(i>self.face.nPoints()-1):
 
137
                        return None
 
138
                if(i==self.face.nPoints()-1):
 
139
                        j = 0
 
140
                else:
 
141
                        j = i+1
 
142
                return LineSegment([ self.face.v[i], self.face.v[j] ])
 
143
        def iterateSegments(self, something):
 
144
                results = []
 
145
                for i in xrange(self.face.nPoints()):
 
146
                        results.extend(something.haveSegment(self.segmentAt(i))) 
 
147
                return results
 
148
        def compareSegments(self, something, segment):
 
149
                results = []
 
150
                for i in xrange(self.face.nPoints()):
 
151
                        results.append(something.compareSegments([self.segmentAt(i), segment]))
 
152
                return results
 
153
 
 
154
class FaceOverlapTest:
 
155
        def __init__(self, face1, face2):
 
156
                self.faces = [face1, face2]
 
157
                self.segments = [ LineSegments(self.faces[0]), LineSegments(self.faces[1]) ]
 
158
        def suspectsOverlap(self):
 
159
                tests = self.segments[0].iterateSegments(self)
 
160
                gi = 0
 
161
                for i in tests:
 
162
                        if( i!=None and i.intersected() ):
 
163
                                gi+=1
 
164
                return gi>0
 
165
        def haveSegment(self, segment):
 
166
                return self.segments[1].compareSegments(self, segment)
 
167
        def compareSegments(self, segments):
 
168
                return segments[0].intersects(segments[1])
 
169
 
 
170
                
 
171
        
 
172
# A fold between two faces with a common edge
 
173
class Fold:
 
174
        ids = -1
 
175
        def __init__(self, parent, refPoly, poly, edge, angle=None):
 
176
                Fold.ids+=1
 
177
                self.id = Fold.ids
 
178
                self.refPoly = refPoly
 
179
                self.poly = poly
 
180
                self.srcFace = None
 
181
                self.desFace = None
 
182
                self.edge = edge
 
183
                self.foldedEdge = edge
 
184
                self.rm = None
 
185
                self.parent = parent
 
186
                self.tree = None
 
187
                if(refPoly!=None):
 
188
                        self.refPolyNormal = refPoly.normal()
 
189
                self.polyNormal = poly.normal()
 
190
                if(angle==None):
 
191
                        self.angle = self.calculateAngle()
 
192
                        self.foldingPoly = poly.rotated(edge, self.angle)
 
193
                else:
 
194
                        self.angle = angle
 
195
                        self.foldingPoly = poly
 
196
                self.unfoldedEdge = self.edge
 
197
                self.unfoldedNormal = None
 
198
                self.animAngle = self.angle
 
199
                self.cr = None
 
200
                self.nancestors = None
 
201
        def reset(self):
 
202
                self.foldingPoly = self.poly.rotated(self.edge, self.dihedralAngle())
 
203
        def getID(self):
 
204
                return self.id
 
205
        def getParent(self):
 
206
                return self.parent
 
207
        def ancestors(self):
 
208
                if(self.nancestors==None):
 
209
                        self.nancestors = self.computeAncestors()
 
210
                return self.nancestors
 
211
        def computeAncestors(self):     
 
212
                if(self.parent==None):
 
213
                        return 0
 
214
                else:
 
215
                        return self.parent.ancestors()+1
 
216
        def dihedralAngle(self):        
 
217
                return self.angle
 
218
        def unfoldTo(self, f):
 
219
                self.animAngle = self.angle*f
 
220
                self.foldingPoly = self.poly.rotated(self.edge, self.animAngle)
 
221
        def calculateAngle(self):
 
222
                sangle = Mathutils.AngleBetweenVecs(self.refPolyNormal, self.polyNormal)
 
223
                if(sangle!=sangle):
 
224
                        sangle=0.0
 
225
                ncp = Mathutils.CrossVecs(self.refPolyNormal, self.polyNormal)
 
226
                dp = Mathutils.DotVecs(ncp, self.edge.vector)
 
227
                if(dp>0.0):
 
228
                        return +sangle
 
229
                else:
 
230
                        return -sangle
 
231
        def alignWithParent(self):
 
232
                pass
 
233
        def unfoldedNormal(self):
 
234
                return self.unfoldedNormal
 
235
        def getEdge(self):
 
236
                return self.edge
 
237
        def getFace(self):
 
238
                return self.poly
 
239
        def testFace(self):
 
240
                return Poly.fromVectors([self.edge.v1, self.edge.v2, Vector([0,0,0])])
 
241
        def unfoldedFace(self):
 
242
                return self.foldingPoly
 
243
        def unfold(self):
 
244
                if(self.parent!=None):
 
245
                        self.parent.foldFace(self)
 
246
        def foldFace(self, child):
 
247
                child.foldingPoly.rotate(self.edge, self.animAngle)             
 
248
                if(self.parent!=None):
 
249
                        self.parent.foldFace(child)
 
250
                        
 
251
class Cut(Fold):
 
252
        pass
 
253
                        
 
254
# Trees build folds by traversing the mesh according to a local measure
 
255
class Tree:
 
256
        def __init__(self, net, parent,fold,otherConstructor=None):
 
257
                self.net = net
 
258
                self.fold = fold
 
259
                self.face = fold.srcFace
 
260
                self.poly = Poly.fromBlenderFace(self.face)
 
261
                self.generations = net.generations
 
262
                self.growing = True
 
263
                self.tooLong = False
 
264
                self.parent = parent
 
265
                self.grown = False
 
266
                if not(otherConstructor):
 
267
                        self.edges = net.edgeIteratorClass(self)
 
268
        def goodness(self):
 
269
                return self.edges.goodness()
 
270
        def compare(self, other):
 
271
                if(self.goodness() > other.goodness()):
 
272
                        return +1
 
273
                else:
 
274
                        return -1
 
275
        def isGrowing(self):
 
276
                return self.growing
 
277
        def beGrowing(self):
 
278
                self.growing = True
 
279
        def grow(self):
 
280
                self.tooLong = self.fold.ancestors()>self.generations
 
281
                if(self.edges.hasNext() and self.growing):
 
282
                        edge = self.edges.next()
 
283
                        tface = self.net.facesAndEdges.takeAdjacentFace(self.face, edge)
 
284
                        if(tface!=None):
 
285
                                self.branch(tface, edge)
 
286
                        if(self.parent==None):
 
287
                                self.grow()
 
288
                else:
 
289
                        self.grown = True
 
290
        def isGrown(self):
 
291
                return self.grown
 
292
        def canGrow(self):
 
293
                return (self.parent!=None and self.parent.grown)
 
294
        def getNet(self):
 
295
                return self.net
 
296
        def getFold(self):
 
297
                return self.fold
 
298
        def getFace(self):
 
299
                return self.face
 
300
        def branch(self, tface, edge):
 
301
                fold = Fold(self.fold, self.poly, Poly.fromBlenderFace(tface), edge)
 
302
                fold.srcFace = tface
 
303
                self.net.myFacesVisited+=1
 
304
                tree = Tree(self.net, self, fold)
 
305
                fold.tree = tree
 
306
                fold.unfold()
 
307
                overlaps = self.net.checkOverlaps(fold)
 
308
                nc = len(overlaps)
 
309
                self.net.overlaps+=nc
 
310
                if(nc>0 and self.net.avoidsOverlaps):
 
311
                        self.handleOverlap(fold, overlaps)
 
312
                else:
 
313
                        self.addFace(fold)
 
314
        def handleOverlap(self, fold, overlaps):
 
315
                self.net.facesAndEdges.returnFace(fold.srcFace)
 
316
                self.net.myFacesVisited-=1
 
317
                for cfold in overlaps:
 
318
                        ttree = cfold.tree
 
319
                        ttree.growing = True
 
320
                        ttree.grow()
 
321
        def addFace(self, fold):
 
322
                ff = fold.unfoldedFace()
 
323
                fold.desFace = self.net.addFace(ff, fold.srcFace)
 
324
                self.net.folds.append(fold)
 
325
                self.net.addBranch(fold.tree)
 
326
                fold.tree.growing = not(self.tooLong)
 
327
                if(self.net.diffuse==False):
 
328
                        fold.tree.grow()
 
329
 
 
330
# A Net is the result of the traversal of the mesh by Trees
 
331
class Net:
 
332
        def __init__(self, src, des):
 
333
                self.src = src
 
334
                self.des = des
 
335
                self.firstFace = None
 
336
                self.firstPoly = None
 
337
                self.refFold = None
 
338
                self.edgeIteratorClass = RandomEdgeIterator
 
339
                if(src!=None):
 
340
                        self.srcFaces = src.faces
 
341
                        self.facesAndEdges = FacesAndEdges(self.src)
 
342
                self.myFacesVisited = 0
 
343
                self.facesAdded = 0
 
344
                self.folds = []
 
345
                self.cuts = []
 
346
                self.branches = []
 
347
                self.overlaps = 0
 
348
                self.avoidsOverlaps = True
 
349
                self.frame = 1
 
350
                self.ff = 180.0
 
351
                self.firstFaceIndex = None
 
352
                self.trees = 0
 
353
                self.foldIPO = None
 
354
                self.perFoldIPO = None
 
355
                self.IPOCurves = {}
 
356
                self.generations = 128
 
357
                self.diffuse = True
 
358
                self.noise = 0.0
 
359
                self.grownBranches = 0
 
360
                self.assignsUV = True
 
361
                self.animates = False
 
362
                self.showProgress = False
 
363
                self.feedback = None
 
364
        def setSelectedFaces(self, faces):
 
365
                self.srcFaces = faces
 
366
                self.facesAndEdges = FacesAndEdges(self.srcFaces)
 
367
        def setShowProgress(self, show):
 
368
                self.showProgress = show
 
369
        # this method really needs work
 
370
        def unfold(self):
 
371
                selectedFaces = [face for face in self.src.faces if (self.src.faceUV and face.sel)]
 
372
                if(self.avoidsOverlaps):
 
373
                        print "unfolding with overlap detection"
 
374
                if(self.firstFaceIndex==None):
 
375
                        self.firstFaceIndex = random.randint(0, len(self.src.faces)-1)
 
376
                else:
 
377
                        print "Using user-selected seed face ", self.firstFaceIndex
 
378
                self.firstFace = self.src.faces[self.firstFaceIndex]
 
379
                z = min([v.co.z for v in self.src.verts])-0.1
 
380
                ff = Poly.fromBlenderFace(self.firstFace)
 
381
                if(len(ff.v)<3):
 
382
                        raise Exception("This mesh contains an isolated edge - it must consist only of faces")
 
383
                testFace = Poly.fromVectors( [ Vector([0.0,0.0,0.0]), Vector([0.0,1.0,0.0]), Vector([1.0,1.0,0.0])  ] )
 
384
                # hmmm
 
385
                u=0
 
386
                v=1
 
387
                w=2
 
388
                if ff.v[u].x==ff.v[u+1].x and ff.v[u].y==ff.v[u+1].y:
 
389
                        u=1
 
390
                        v=2
 
391
                        w=0
 
392
                # here we make a couple of folds, not part of the net, which serve to get the net into the xy plane
 
393
                xyFace = Poly.fromList( [ [ff.v[u].x,ff.v[u].y, z] , [ff.v[v].x,ff.v[v].y, z] , [ff.v[w].x+0.1,ff.v[w].y+0.1, z] ] )
 
394
                refFace = Poly.fromVectors([ ff.v[u], ff.v[v], xyFace.v[1], xyFace.v[0] ] )
 
395
                xyFold =  Fold(None,   xyFace, refFace, Edge(xyFace.v[0], xyFace.v[1] ))
 
396
                self.refFold = Fold(xyFold, refFace, ff,         Edge(refFace.v[0], refFace.v[1] ))
 
397
                self.refFold.srcFace = self.firstFace
 
398
                trunk = Tree(self, None, self.refFold)
 
399
                trunk.generations = self.generations
 
400
                self.firstPoly = ff
 
401
                self.facesAndEdges.takeFace(self.firstFace)
 
402
                self.myFacesVisited+=1
 
403
                self.refFold.unfold()
 
404
                self.refFold.tree = trunk
 
405
                self.refFold.desFace = self.addFace(self.refFold.unfoldedFace(), self.refFold.srcFace)
 
406
                self.folds.append(self.refFold)
 
407
                trunk.grow()
 
408
                i = 0
 
409
                while(self.myFacesVisited<len(self.src.faces) and len(self.branches) > 0):
 
410
                        if self.edgeIteratorClass==RandomEdgeIterator:
 
411
                                i = random.randint(0,len(self.branches)-1)
 
412
                        tree = self.branches[i]
 
413
                        if(tree.isGrown()):
 
414
                                self.branches.pop(i)
 
415
                        else:
 
416
                                tree.beGrowing()
 
417
                                if(tree.canGrow()):
 
418
                                        tree.grow()
 
419
                                        i = 0
 
420
                                else:
 
421
                                        i = (i + 1) % len(self.branches)
 
422
                if self.src.faceUV:
 
423
                        for face in self.src.faces:
 
424
                                face.sel = False
 
425
                        for face in selectedFaces:
 
426
                                face.sel = True
 
427
                self.src.update()
 
428
                Window.RedrawAll()
 
429
        def assignUVs(self):
 
430
                for fold in self.folds:
 
431
                        self.assignUV(fold.srcFace, fold.unfoldedFace())
 
432
                print " assigned uv to ", len(self.folds), len(self.src.faces)
 
433
                self.src.update()
 
434
        def checkOverlaps(self, fold):
 
435
                #return self.getOverlapsBetween(fold, self.folds)
 
436
                return self.getOverlapsBetweenGL(fold, self.folds)
 
437
        def getOverlapsBetween(self, fold, folds):
 
438
                if(fold.parent==None):
 
439
                        return []
 
440
                mf = fold.unfoldedFace()
 
441
                c = []
 
442
                for afold in folds:
 
443
                        mdf = afold.unfoldedFace()
 
444
                        if(afold!=fold):
 
445
                                it1 = FaceOverlapTest(mf, mdf)
 
446
                                it2 = FaceOverlapTest(mdf, mf)
 
447
                                overlap = (it1.suspectsOverlap() or it2.suspectsOverlap())
 
448
                                inside = ( mdf.containsAnyOf(mf) or mf.containsAnyOf(mdf) )
 
449
                                if(  overlap or inside or mdf.overlays(mf)):
 
450
                                        c.append(afold)
 
451
                return c
 
452
        def getOverlapsBetweenGL(self, fold, folds):
 
453
                b = fold.unfoldedFace().bounds()
 
454
                polys = len(folds)*4+16 # the buffer is nhits, mindepth, maxdepth, name
 
455
                buffer = BGL.Buffer(BGL.GL_INT, polys)
 
456
                BGL.glSelectBuffer(polys, buffer)
 
457
                BGL.glRenderMode(BGL.GL_SELECT)
 
458
                BGL.glInitNames()
 
459
                BGL.glPushName(0)
 
460
                BGL.glPushMatrix()
 
461
                BGL.glMatrixMode(BGL.GL_PROJECTION)
 
462
                BGL.glLoadIdentity()
 
463
                BGL.glOrtho(b[0].x, b[1].x, b[1].y, b[0].y, 0.0, 10.0)
 
464
                #clip = BGL.Buffer(BGL.GL_FLOAT, 4)
 
465
                #clip.list = [0,0,0,0]
 
466
                #BGL.glClipPlane(BGL.GL_CLIP_PLANE1, clip)
 
467
                # could use clipping planes here too
 
468
                BGL.glMatrixMode(BGL.GL_MODELVIEW)
 
469
                BGL.glLoadIdentity()
 
470
                bx = (b[1].x - b[0].x)
 
471
                by = (b[1].y - b[0].y)
 
472
                cx = bx / 2.0
 
473
                cy = by / 2.0
 
474
                for f in xrange(len(folds)):
 
475
                        afold = folds[f]
 
476
                        if(fold!=afold):
 
477
                                BGL.glLoadName(f)
 
478
                                BGL.glBegin(BGL.GL_LINE_LOOP)
 
479
                                for v in afold.unfoldedFace().v:
 
480
                                        BGL.glVertex2f(v.x, v.y)
 
481
                                BGL.glEnd()
 
482
                BGL.glPopMatrix()
 
483
                BGL.glFlush()
 
484
                hits = BGL.glRenderMode(BGL.GL_RENDER)
 
485
                buffer = [buffer[i] for i in xrange(3, 4*hits, 4)]
 
486
                o = [folds[buffer[i]] for i in xrange(len(buffer))]
 
487
                return self.getOverlapsBetween(fold, o)
 
488
        def colourFace(self, face, cr):
 
489
                for c in face.col:
 
490
                        c.r = int(cr[0])
 
491
                        c.g = int(cr[1])
 
492
                        c.b = int(cr[2])
 
493
                        c.a = int(cr[3])
 
494
                self.src.update()
 
495
        def setAvoidsOverlaps(self, avoids):
 
496
                self.avoidsOverlaps = avoids
 
497
        def addBranch(self, branch):
 
498
                self.branches.append(branch)
 
499
                if self.edgeIteratorClass!=RandomEdgeIterator:
 
500
                        self.branches.sort(lambda b1, b2: b1.compare(b2))
 
501
        def srcSize(self):
 
502
                return len(self.src.faces)
 
503
        def nBranches(self):
 
504
                return len(self.branches)
 
505
        def facesCreated(self):
 
506
                return len(self.des.faces)
 
507
        def facesVisited(self):
 
508
                return self.myFacesVisited
 
509
        def getOverlaps(self):
 
510
                return self.overlaps
 
511
        def sortOutIPOSource(self):
 
512
                print "Sorting out IPO"
 
513
                if self.foldIPO!=None:
 
514
                        return
 
515
                o = None
 
516
                try:
 
517
                        o = Blender.Object.Get("FoldRate")
 
518
                except:
 
519
                        o = Blender.Object.New("Empty", "FoldRate")
 
520
                        Blender.Scene.GetCurrent().objects.link(o)
 
521
                if(o.getIpo()==None):
 
522
                        ipo = Blender.Ipo.New("Object", "FoldRateIPO")
 
523
                        z = ipo.addCurve("RotZ")
 
524
                        print " added RotZ IPO curve"
 
525
                        z.addBezier((1,0))
 
526
                        # again, why is this 10x out ?
 
527
                        z.addBezier((180, self.ff/10.0))
 
528
                        z.addBezier((361, 0.0))
 
529
                        o.setIpo(ipo)
 
530
                        z.recalc()
 
531
                        z.setInterpolation("Bezier")
 
532
                        z.setExtrapolation("Cyclic")
 
533
                self.setIPOSource(o)
 
534
                print " added IPO source"
 
535
        def setIPOSource(self, object):
 
536
                try:
 
537
                        self.foldIPO = object
 
538
                        for i in xrange(self.foldIPO.getIpo().getNcurves()):
 
539
                                self.IPOCurves[self.foldIPO.getIpo().getCurves()[i].getName()] = i
 
540
                                print " added ", self.foldIPO.getIpo().getCurves()[i].getName()
 
541
                except:
 
542
                        print "Problem setting IPO object"
 
543
                        print sys.exc_info()[1]
 
544
                        traceback.print_exc(file=sys.stdout)
 
545
        def setFoldFactor(self, ff):
 
546
                self.ff = ff
 
547
        def sayTree(self):
 
548
                for fold in self.folds:
 
549
                        if(fold.getParent()!=None):
 
550
                                print fold.getID(), fold.dihedralAngle(), fold.getParent().getID()
 
551
        def report(self):
 
552
                p = int(float(self.myFacesVisited)/float(len(self.src.faces)) * 100)
 
553
                print str(p) + "% unfolded"
 
554
                print "faces created:", self.facesCreated()
 
555
                print "faces visited:", self.facesVisited()
 
556
                print "originalfaces:", len(self.src.faces)
 
557
                n=0
 
558
                if(self.avoidsOverlaps):
 
559
                        print "net avoided at least ", self.getOverlaps(), " overlaps ",
 
560
                        n = len(self.src.faces) - self.facesCreated()
 
561
                        if(n>0):
 
562
                                print "but was unable to avoid ", n, " overlaps. Incomplete net."
 
563
                        else:
 
564
                                print "- A complete net."
 
565
                else:
 
566
                        print "net has at least ", self.getOverlaps(), " collision(s)"
 
567
                return n
 
568
        # fold all my folds to a fraction of their total fold angle
 
569
        def unfoldToCurrentFrame(self):
 
570
                self.unfoldTo(Blender.Scene.GetCurrent().getRenderingContext().currentFrame())
 
571
        def unfoldTo(self, frame):
 
572
                frames = Blender.Scene.GetCurrent().getRenderingContext().endFrame()
 
573
                if(self.foldIPO!=None and self.foldIPO.getIpo()!=None):
 
574
                        f = self.foldIPO.getIpo().EvaluateCurveOn(self.IPOCurves["RotZ"],frame)
 
575
                        # err, this number seems to be 10x less than it ought to be
 
576
                        fff = 1.0 - (f*10.0 / self.ff)
 
577
                else:
 
578
                        fff = 1.0-((frame)/(frames*1.0))
 
579
                for fold in self.folds:
 
580
                        fold.unfoldTo(fff)
 
581
                for fold in self.folds:
 
582
                        fold.unfold()
 
583
                        tface = fold.unfoldedFace()
 
584
                        bface = fold.desFace
 
585
                        i = 0
 
586
                        for v in bface.verts:
 
587
                                v.co.x = tface.v[i].x
 
588
                                v.co.y = tface.v[i].y
 
589
                                v.co.z = tface.v[i].z
 
590
                                i+=1
 
591
                Window.Redraw(Window.Types.VIEW3D)
 
592
                return None
 
593
        def addFace(self, poly, originalFace=None):
 
594
                originalLength = len(self.des.verts)
 
595
                self.des.verts.extend([Vector(vv.x, vv.y, vv.z) for vv in poly.v])
 
596
                self.des.faces.extend([ range(originalLength, originalLength + poly.size()) ])
 
597
                newFace = self.des.faces[len(self.des.faces)-1]
 
598
                newFace.uv = [vv for vv in poly.v]
 
599
                if(originalFace!=None and self.src.vertexColors):
 
600
                        newFace.col = [c for c in originalFace.col]
 
601
                if(self.feedback!=None):
 
602
                        pu = str(int(self.fractionUnfolded() * 100))+"% unfolded"
 
603
                        howMuchDone = str(self.myFacesVisited)+" of "+str(len(self.src.faces))+"  "+pu
 
604
                        self.feedback.say(howMuchDone)
 
605
                        #Window.DrawProgressBar (p, pu)
 
606
                if(self.showProgress):
 
607
                        Window.Redraw(Window.Types.VIEW3D)
 
608
                return newFace
 
609
        def fractionUnfolded(self):
 
610
                return float(self.myFacesVisited)/float(len(self.src.faces))
 
611
        def assignUV(self, face, uv):
 
612
                face.uv = [Vector(v.x, v.y) for v in uv.v]
 
613
        def unfoldAll(feedback=None):
 
614
                objects = Blender.Object.Get()
 
615
                for object in objects:
 
616
                        if(object.getType()=='Mesh' and not(object.getName().endswith("_net")) and len(object.getData(False, True).faces)>1):
 
617
                                net = Net.createNet(object, feedback)
 
618
                                net.searchForUnfolding()
 
619
                                svg = SVGExporter(net, object.getName()+".svg")
 
620
                                svg.export()
 
621
        unfoldAll = staticmethod(unfoldAll)
 
622
        def searchForUnfolding(self, limit=-1):
 
623
                overlaps = 1
 
624
                attempts = 0
 
625
                while(overlaps > 0 or attempts<limit):
 
626
                        self.unfold()
 
627
                        overlaps = self.report()
 
628
                        attempts+=1
 
629
                return attempts
 
630
        def unfoldSelected(feedback=None, netName=None):
 
631
                return Net.createNet(Blender.Object.GetSelected()[0], feedback, netName)
 
632
        unfoldSelected = staticmethod(unfoldSelected)
 
633
        def clone(self, object=None):
 
634
                if(object==None):
 
635
                        object = self.object
 
636
                net = Net.createNet(object, self.feedback)
 
637
                net.avoidsOverlaps = net.avoidsOverlaps
 
638
                return net
 
639
        def createNet(ob, feedback=None, netName=None):
 
640
                mesh = ob.getData(mesh=1)
 
641
                netObject = None
 
642
                if(netName==None):
 
643
                        netName = ob.name[0:16]+"_net"
 
644
                try:
 
645
                        netObject = Blender.Object.Get(netName)
 
646
                        netMesh = netObject.getData(False, True)
 
647
                        if(netMesh!=None):
 
648
                                netMesh.verts = None # clear the mesh
 
649
                        else:
 
650
                                netObject = Blender.Object.New("Mesh", netName)
 
651
                except:
 
652
                        if(netObject==None):
 
653
                                netObject = Blender.Object.New("Mesh", netName)
 
654
                netMesh = netObject.getData(False, True)  # True means "as a Mesh not an NMesh"
 
655
                try:
 
656
                        Blender.Scene.GetCurrent().objects.link(netObject)
 
657
                except:
 
658
                        pass
 
659
                try:
 
660
                        netMesh.materials = mesh.materials
 
661
                        netMesh.vertexColors = True
 
662
                except:
 
663
                        print "Problem setting materials here"
 
664
                net = Net(mesh, netMesh)
 
665
                if mesh.faceUV and mesh.activeFace>=0 and (mesh.faces[mesh.activeFace].sel):
 
666
                        net.firstFaceIndex = mesh.activeFace
 
667
                net.object = ob
 
668
                net.feedback = feedback
 
669
                return net
 
670
        createNet = staticmethod(createNet)
 
671
        def importNet(filename):
 
672
                netName = filename.rstrip(".svg").replace("\\","/")
 
673
                netName = netName[netName.rfind("/")+1:]
 
674
                try:
 
675
                        netObject = Blender.Object.Get(netName)
 
676
                except:
 
677
                        netObject  = Blender.Object.New("Mesh", netName)
 
678
                netObject.getData(mesh=1).name = netName
 
679
                try:
 
680
                        Blender.Scene.GetCurrent().objects.link(netObject)
 
681
                except:
 
682
                        pass
 
683
                net = Net(None, netObject.getData(mesh=1))
 
684
                handler = NetHandler(net)
 
685
                xml.sax.parse(filename, handler)
 
686
                Window.Redraw(Window.Types.VIEW3D)
 
687
                return net
 
688
        importNet = staticmethod(importNet)
 
689
        def getSourceMesh(self):
 
690
                return self.src
 
691
                
 
692
# determines the order in which to visit faces according to a local measure             
 
693
class EdgeIterator:
 
694
        def __init__(self, branch, otherConstructor=None):
 
695
                self.branch = branch
 
696
                self.bface = branch.getFace()
 
697
                self.edge = branch.getFold().getEdge()
 
698
                self.net = branch.getNet()
 
699
                self.n = len(self.bface)
 
700
                self.edges = []
 
701
                self.i = 0
 
702
                self.gooodness = 0
 
703
                self.createEdges()
 
704
                self.computeGoodness()
 
705
                if(otherConstructor==None):
 
706
                        self.sequenceEdges()
 
707
        def createEdges(self):
 
708
                edge = None
 
709
                e = Edge.edgesOfBlenderFace(self.net.getSourceMesh(), self.bface)
 
710
                for edge in e:
 
711
                        if not(edge.isBlenderSeam() and edge!=self.edge):
 
712
                                self.edges.append(edge)
 
713
        def sequenceEdges(self):
 
714
                pass
 
715
        def next(self):
 
716
                edge = self.edges[self.i]
 
717
                self.i+=1
 
718
                return edge
 
719
        def size(self):
 
720
                return len(self.edges)
 
721
        def reset(self):
 
722
                self.i = 0
 
723
        def hasNext(self):
 
724
                return (self.i<len(self.edges))
 
725
        def goodness(self):
 
726
                return self.gooodness
 
727
        def computeGoodness(self):
 
728
                self.gooodness = 0
 
729
        def rotate(self):
 
730
                self.edges.append(self.edges.pop(0))
 
731
 
 
732
class RandomEdgeIterator(EdgeIterator):
 
733
        def sequenceEdges(self):
 
734
                random.seed()
 
735
                random.shuffle(self.edges)
 
736
        def goodness(self):
 
737
                return random.randint(0, self.net.srcSize())
 
738
        
 
739
                
 
740
class Largest(EdgeIterator):
 
741
        def sequenceEdges(self):
 
742
                for e in self.edges:
 
743
                        f = self.net.facesAndEdges.findAdjacentFace(self.bface, e)
 
744
                        if(f!=None):
 
745
                                e.setGoodness(f.area)
 
746
                self.edges.sort(lambda e1, e2: -e1.compare(e2))
 
747
        def computeGoodness(self):
 
748
                self.gooodness = self.bface.area
 
749
                
 
750
                        
 
751
class Brightest(EdgeIterator):
 
752
        def sequenceEdges(self):
 
753
                for edge in self.edges:
 
754
                        f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
 
755
                        if(f!=None):
 
756
                                b = 0
 
757
                                if self.net.src.vertexColors:
 
758
                                        for c in f.col:
 
759
                                                b+=(c.g+c.r+c.b)
 
760
                                rc = float(random.randint(0, self.net.srcSize())) / float(self.net.srcSize()) / 100.0
 
761
                                b+=rc
 
762
                                edge.setGoodness(b)
 
763
                self.edges.sort(lambda e1, e2: e1.compare(e2))
 
764
        def computeGoodness(self):
 
765
                g = 0
 
766
                if self.net.src.vertexColors:
 
767
                        for c in self.bface.col:
 
768
                                g+=(c.g+c.r+c.b)
 
769
                self.gooodness = g
 
770
                
 
771
class OddEven(EdgeIterator):
 
772
        i = True
 
773
        def sequenceEdges(self):
 
774
                OddEven.i = not(OddEven.i)
 
775
                if(OddEven.i):
 
776
                        self.edges.reverse()
 
777
                
 
778
class Curvature(EdgeIterator):
 
779
        def sequenceEdges(self):
 
780
                p1 = Poly.fromBlenderFace(self.bface)
 
781
                gg = 0.0
 
782
                for edge in self.edges:
 
783
                        f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
 
784
                        if(f!=None):
 
785
                                p2 = Poly.fromBlenderFace(f)
 
786
                                fold = Fold(None, p1, p2, edge)
 
787
                                fold.srcFace = f
 
788
                                b = Tree(self.net, self.branch, fold, self)
 
789
                                c = Curvature(b, False)
 
790
                                g = c.goodness()
 
791
                                gg+=g
 
792
                                edge.setGoodness(g)
 
793
                self.edges.sort(lambda e1, e2: e1.compare(e2))
 
794
                tg = (self.gooodness + gg)
 
795
                rc = float(random.randint(0, self.net.srcSize())) / float(self.net.srcSize()) / 100.0
 
796
                if(tg!=0.0):
 
797
                        self.gooodness = self.gooodness + rc / tg
 
798
        def computeGoodness(self):
 
799
                g = 0
 
800
                for edge in self.edges:
 
801
                        f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
 
802
                        if(f!=None):
 
803
                                p1 = Poly.fromBlenderFace(self.bface)
 
804
                                p2 = Poly.fromBlenderFace(f)
 
805
                                f = Fold(None, p1, p2, edge)
 
806
                                g += f.dihedralAngle()
 
807
                self.gooodness = g
 
808
                
 
809
        
 
810
class Edge:
 
811
        def __init__(self, v1=None, v2=None, mEdge=None, i=-1):
 
812
                self.idx = i
 
813
                if v1 and v2:
 
814
                        self.v1 = v1.copy()
 
815
                        self.v2 = v2.copy()
 
816
                else:
 
817
                        self.v1 = mEdge.v1.co.copy()
 
818
                        self.v2 = mEdge.v2.co.copy()
 
819
                self.v1n = -self.v1
 
820
                self.vector = self.v1-self.v2
 
821
                self.vector.resize3D()
 
822
                self.vector.normalize()
 
823
                self.bmEdge = mEdge
 
824
                self.gooodness = 0.0
 
825
        def fromBlenderFace(mesh, bface, i):
 
826
                if(i>len(bface)-1):
 
827
                        return None
 
828
                if(i==len(bface)-1):
 
829
                        j = 0
 
830
                else:
 
831
                        j = i+1
 
832
                edge =  Edge( bface.v[i].co.copy(), bface.v[j].co.copy() )
 
833
                edge.bEdge = mesh.findEdge(bface.v[i], bface.v[j])
 
834
                edge.idx = i
 
835
                return edge
 
836
        fromBlenderFace=staticmethod(fromBlenderFace)
 
837
        def edgesOfBlenderFace(mesh, bmFace):
 
838
                edges = [mesh.edges[mesh.findEdges(edge[0], edge[1])] for edge in bmFace.edge_keys]
 
839
                v = bmFace.verts
 
840
                e = []
 
841
                vi = v[0]
 
842
                i=0
 
843
                for j in xrange(1, len(bmFace)+1):
 
844
                        vj = v[j%len(bmFace)]
 
845
                        for ee in edges:
 
846
                                if((ee.v1.index==vi.index and ee.v2.index==vj.index) or (ee.v2.index==vi.index and ee.v1.index==vj.index)):
 
847
                                        e.append(Edge(vi.co, vj.co, ee, i))
 
848
                                        i+=1
 
849
                        vi = vj
 
850
                return e
 
851
        edgesOfBlenderFace=staticmethod(edgesOfBlenderFace)
 
852
        def isBlenderSeam(self):
 
853
                return (self.bmEdge.flag & Mesh.EdgeFlags.SEAM)
 
854
        def isInFGon(self):
 
855
                return (self.bmEdge.flag & Mesh.EdgeFlags.FGON)
 
856
        def mapTo(self, poly):
 
857
                if(self.idx==len(poly.v)-1):
 
858
                        j = 0
 
859
                else:
 
860
                        j = self.idx+1
 
861
                return Edge(poly.v[self.idx], poly.v[j])
 
862
        def isDegenerate(self):
 
863
                return self.vector.length==0
 
864
        def vertices(s):
 
865
                return [ [s.v1.x, s.v1.y, s.v1.z], [s.v2.x, s.v2.y,s.v2.z] ]
 
866
        def key(self):
 
867
                return self.bmEdge.key
 
868
        def goodness(self):
 
869
                return self.gooodness
 
870
        def setGoodness(self, g):
 
871
                self.gooodness = g
 
872
        def compare(self, other):
 
873
                if(self.goodness() > other.goodness()):
 
874
                        return +1
 
875
                else:
 
876
                        return -1
 
877
        
 
878
class Poly:
 
879
        ids = -1
 
880
        def __init__(self):
 
881
                Poly.ids+=1
 
882
                self.v = []
 
883
                self.id = Poly.ids
 
884
                self.boundz = None
 
885
        def getID(self):
 
886
                return self.id
 
887
        def normal(self):
 
888
                a =self.v[0]
 
889
                b=self.v[1]
 
890
                c=self.v[2]
 
891
                p = b-a
 
892
                p.resize3D()
 
893
                q = a-c
 
894
                q.resize3D()
 
895
                return CrossVecs(p,q)
 
896
        def isBad(self):
 
897
                badness = 0
 
898
                for vv in self.v:
 
899
                        if(vv.x!=vv.x or vv.y!=vv.y or vv.z!=vv.z): # Nan check
 
900
                                badness+=1
 
901
                return (badness>0)
 
902
        def midpoint(self):
 
903
                x=y=z = 0.0
 
904
                n = 0
 
905
                for vv in self.v:
 
906
                        x+=vv.x
 
907
                        y+=vv.y
 
908
                        z+=vv.z
 
909
                        n+=1
 
910
                return [ x/n, y/n, z/n ]
 
911
        def centerAtOrigin(self):
 
912
                mp = self.midpoint()
 
913
                mp = -mp
 
914
                toOrigin = TranslationMatrix(mp)
 
915
                self.v = [(vv * toOrigin) for vv in self.v]
 
916
        def move(self, tv):
 
917
                mv = TranslationMatrix(tv)
 
918
                self.v = [(vv * mv) for vv in self.v]
 
919
        def scale(self, s):
 
920
                mp = Vector(self.midpoint())
 
921
                fromOrigin = TranslationMatrix(mp)
 
922
                mp = -mp
 
923
                toOrigin = TranslationMatrix(mp)
 
924
                sm = ScaleMatrix(s, 4)
 
925
                # Todo, the 3 lines below in 1 LC
 
926
                self.v = [(vv * toOrigin) for vv in self.v]
 
927
                self.v = [(sm * vv) for vv in self.v]
 
928
                self.v = [(vv * fromOrigin) for vv in self.v]
 
929
        def nPoints(self):
 
930
                return len(self.v)
 
931
        def size(self):
 
932
                return len(self.v)
 
933
        def rotated(self, axis, angle):
 
934
                p = self.clone()
 
935
                p.rotate(axis, angle)
 
936
                return p
 
937
        def rotate(self, axis, angle):
 
938
                rotation = RotationMatrix(angle, 4, "r", axis.vector)
 
939
                toOrigin = TranslationMatrix(axis.v1n)
 
940
                fromOrigin = TranslationMatrix(axis.v1)
 
941
                # Todo, the 3 lines below in 1 LC
 
942
                self.v = [(vv * toOrigin) for vv in self.v]
 
943
                self.v = [(rotation * vv) for vv in self.v]
 
944
                self.v = [(vv * fromOrigin) for vv in self.v]
 
945
        def moveAlong(self, vector, distance):
 
946
                t = TranslationMatrix(vector)
 
947
                s = ScaleMatrix(distance, 4)
 
948
                ts = t*s
 
949
                self.v = [(vv * ts) for vv in self.v]
 
950
        def bounds(self):
 
951
                if(self.boundz == None):
 
952
                        vv = [vv for vv in self.v]
 
953
                        vv.sort(key=lambda v: v.x)
 
954
                        minx = vv[0].x
 
955
                        maxx = vv[len(vv)-1].x
 
956
                        vv.sort(key=lambda v: v.y)
 
957
                        miny = vv[0].y
 
958
                        maxy = vv[len(vv)-1].y
 
959
                        self.boundz = [Vector(minx, miny, 0), Vector(maxx, maxy, 0)]
 
960
                return self.boundz
 
961
        def fromBlenderFace(bface):
 
962
                p = Poly()
 
963
                for vv in bface.v:
 
964
                        vec = Vector([vv.co[0], vv.co[1], vv.co[2] , 1.0]) 
 
965
                        p.v.append(vec)
 
966
                return p
 
967
        fromBlenderFace = staticmethod(fromBlenderFace)
 
968
        def fromList(list):
 
969
                p = Poly()
 
970
                for vv in list:
 
971
                        vec = Vector( [vvv for vvv in vv] )
 
972
                        vec.resize4D()
 
973
                        p.v.append(vec)
 
974
                return p
 
975
        fromList = staticmethod(fromList)
 
976
        def fromVectors(vectors):
 
977
                p = Poly()
 
978
                p.v.extend([v.copy().resize4D() for v in vectors])
 
979
                return p
 
980
        fromVectors = staticmethod(fromVectors)
 
981
        def clone(self):
 
982
                p = Poly()
 
983
                p.v.extend(self.v)
 
984
                return p
 
985
        def hasVertex(self, ttv):
 
986
                v = Mathutils.Vector(ttv)
 
987
                v.normalize()
 
988
                for tv in self.v:
 
989
                        vv = Mathutils.Vector(tv)
 
990
                        vv.normalize()
 
991
                        t = 0.00001
 
992
                        if abs(vv.x-v.x)<t and abs(vv.y-v.y)<t:
 
993
                                return True
 
994
                return False
 
995
        def overlays(self, poly):
 
996
                if len(poly.v)!=len(self.v):
 
997
                        return False
 
998
                c = 0
 
999
                for point in poly.v:
 
1000
                        if self.hasVertex(point):
 
1001
                                c+=1
 
1002
                return c==len(self.v)
 
1003
        def sharesVertexWith(self, poly):
 
1004
                for point in poly.v:
 
1005
                        if(self.hasVertex(point)):
 
1006
                                return True
 
1007
                return False
 
1008
        def containsAnyOf(self, poly):
 
1009
                for point in poly.v:
 
1010
                        if(not(self.hasVertex(point))):
 
1011
                                if self.contains(point):
 
1012
                                        return True
 
1013
                return False
 
1014
        def toString(self):
 
1015
                return self.v
 
1016
        # This is the BEST algorithm for point-in-polygon detection.
 
1017
        # It's by W. Randolph Franklin. It's also very beautiful (looks even better in C).
 
1018
        # All the others are shite; they give false positives.
 
1019
        # returns 1 for inside, 1 or 0 for edges
 
1020
        def contains(self, tp):
 
1021
                c = 0
 
1022
                j = len(self.v)-1
 
1023
                for i in xrange(len(self.v)):
 
1024
                        if(i>0): j=i-1
 
1025
                        cv = self.v[i]
 
1026
                        nv = self.v[j]
 
1027
                        if ((((cv.y<=tp.y) and (tp.y<nv.y)) or ((nv.y<=tp.y) and (tp.y<cv.y))) and (tp.x < (nv.x - cv.x) * (tp.y - cv.y) / (nv.y - cv.y) + cv.x)):
 
1028
                                c = not(c)
 
1029
                return (c == 1)
 
1030
                
 
1031
class SVGExporter:
 
1032
        def __init__(self, net, filename):
 
1033
                self.net = net
 
1034
                print self.net.des.name
 
1035
                self.object = self.net.object
 
1036
                print "Exporting ", self.object
 
1037
                self.filename = filename
 
1038
                self.file = None
 
1039
                self.e = None
 
1040
                self.width = 1024
 
1041
                self.height = 768
 
1042
        def start(self):
 
1043
                print "Exporting SVG to ", self.filename
 
1044
                self.file = open(self.filename, 'w')
 
1045
                self.e = xml.sax.saxutils.XMLGenerator(self.file, "UTF-8")
 
1046
                atts = {}
 
1047
                atts["width"] = "100%"
 
1048
                atts["height"] = "100%"
 
1049
                atts["viewBox"] = str(self.vxmin)+" "+str(self.vymin)+" "+str(self.vxmax-self.vxmin)+" "+str(self.vymax-self.vymin)
 
1050
                atts["xmlns:nets"] = "http://celeriac.net/unfolder/rdf#"
 
1051
                atts["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
 
1052
                atts["xmlns"] ="http://www.w3.org/2000/svg"
 
1053
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1054
                self.e.startDocument()
 
1055
                self.e.startElement("svg", a)
 
1056
                self.e.startElement("defs", xml.sax.xmlreader.AttributesImpl({}))
 
1057
                atts = {}
 
1058
                atts["type"]="text/css"
 
1059
                self.e.startElement("style", atts)
 
1060
                # can't find a proper way to do this
 
1061
                self.file.write("<![CDATA[")
 
1062
                self.file.write("polygon.poly{fill:white;stroke:black;stroke-width: 0.001}")
 
1063
                self.file.write("g#foldLines line.valley{stroke:white;stroke-width:0.01;stroke-dasharray:0.02,0.01,0.02,0.05}")
 
1064
                self.file.write("g#foldLines line.mountain{stroke:white;stroke-width:0.01;stroke-dasharray:0.02,0.04}")
 
1065
                self.file.write("]]>")
 
1066
                self.e.endElement("style")
 
1067
                self.e.endElement("defs")
 
1068
                #self.addClipPath()
 
1069
                self.addMeta()
 
1070
        def addMeta(self):
 
1071
                self.e.startElement("metadata", xml.sax.xmlreader.AttributesImpl({}))
 
1072
                self.e.startElement("nets:net", xml.sax.xmlreader.AttributesImpl({}))
 
1073
                for i in xrange(1, len(self.net.folds)):
 
1074
                        fold = self.net.folds[i]
 
1075
                        # AttributesNSImpl - documentation is rubbish. using this hack.
 
1076
                        atts = {}
 
1077
                        atts["nets:id"] = "fold"+str(fold.getID())
 
1078
                        if(fold.parent!=None):
 
1079
                                atts["nets:parent"] = "fold"+str(fold.parent.getID())
 
1080
                        else:
 
1081
                                atts["nets:parent"] = "null"
 
1082
                        atts["nets:da"] = str(fold.dihedralAngle())
 
1083
                        if(fold.parent!=None):
 
1084
                                atts["nets:ofPoly"] = "poly"+str(fold.parent.foldingPoly.getID())
 
1085
                        else:
 
1086
                                atts["nets:ofPoly"] = ""
 
1087
                        atts["nets:toPoly"] = "poly"+str(fold.foldingPoly.getID())
 
1088
                        a = xml.sax.xmlreader.AttributesImpl(atts)
 
1089
                        self.e.startElement("nets:fold",  a)
 
1090
                        self.e.endElement("nets:fold")
 
1091
                self.e.endElement("nets:net")
 
1092
                self.e.endElement("metadata")
 
1093
        def end(self):
 
1094
                self.e.endElement("svg")
 
1095
                self.e.endDocument()
 
1096
                print "grown."
 
1097
        def export(self):
 
1098
                self.net.unfoldTo(1)
 
1099
                bb = self.object.getBoundBox()
 
1100
                self.vxmin = bb[0][0]
 
1101
                self.vymin = bb[0][1]
 
1102
                self.vxmax = bb[7][0]
 
1103
                self.vymax = bb[7][1]
 
1104
                self.start()
 
1105
                atts = {}
 
1106
                atts["id"] = self.object.getName()
 
1107
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1108
                self.e.startElement("g", a)
 
1109
                #self.addUVImage()
 
1110
                self.addPolys()
 
1111
                self.addFoldLines()
 
1112
                #self.addCutLines()
 
1113
                self.e.endElement("g")
 
1114
                self.end()
 
1115
        def addClipPath(self):
 
1116
                atts = {}
 
1117
                atts["id"] = "netClip"
 
1118
                atts["clipPathUnits"] = "userSpaceOnUse"
 
1119
                atts["x"] = str(self.vxmin)
 
1120
                atts["y"] = str(self.vymin)
 
1121
                atts["width"] = "100%"
 
1122
                atts["height"] = "100%"
 
1123
                self.e.startElement("clipPath", atts)
 
1124
                self.addPolys()
 
1125
                self.e.endElement("clipPath")
 
1126
        def addUVImage(self):
 
1127
                image = Blender.Image.GetCurrent()
 
1128
                if image==None:
 
1129
                        return
 
1130
                ifn = image.getFilename()
 
1131
                #ifn = self.filename.replace(".svg", ".jpg")
 
1132
                #image.setFilename(ifn)
 
1133
                #ifn = ifn[ifn.rfind("/")+1:]
 
1134
                #image.save()
 
1135
                atts = {}
 
1136
                atts["clip-path"] = "url(#netClip)"
 
1137
                atts["xlink:href"] = ifn
 
1138
                self.e.startElement("image", atts)
 
1139
                self.e.endElement("image")
 
1140
        def addPolys(self):
 
1141
                atts = {}
 
1142
                atts["id"] = "polys"
 
1143
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1144
                self.e.startElement("g", a)
 
1145
                for i in xrange(len(self.net.folds)):
 
1146
                        self.addPoly(self.net.folds[i])
 
1147
                self.e.endElement("g")
 
1148
        def addFoldLines(self):
 
1149
                atts = {}
 
1150
                atts["id"] = "foldLines"
 
1151
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1152
                self.e.startElement("g", a)
 
1153
                for i in xrange( 1, len(self.net.folds)):
 
1154
                        self.addFoldLine(self.net.folds[i])
 
1155
                self.e.endElement("g")
 
1156
        def addFoldLine(self, fold):
 
1157
                edge = fold.edge.mapTo(fold.parent.foldingPoly)
 
1158
                if fold.dihedralAngle()>0:
 
1159
                        foldType="valley"
 
1160
                else:
 
1161
                        foldType="mountain"
 
1162
                atts={}
 
1163
                atts["x1"] = str(edge.v1.x)
 
1164
                atts["y1"] = str(edge.v1.y)
 
1165
                atts["x2"] = str(edge.v2.x)
 
1166
                atts["y2"] = str(edge.v2.y)
 
1167
                atts["id"] = "fold"+str(fold.getID())
 
1168
                atts["class"] = foldType
 
1169
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1170
                self.e.startElement("line", a)
 
1171
                self.e.endElement("line")
 
1172
        def addCutLines(self):
 
1173
                atts = {}
 
1174
                atts["id"] = "cutLines"
 
1175
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1176
                self.e.startElement("g", a)
 
1177
                for i in xrange( 1, len(self.net.cuts)):
 
1178
                        self.addCutLine(self.net.cuts[i])
 
1179
                self.e.endElement("g")
 
1180
        def addCutLine(self, cut):
 
1181
                edge = cut.edge.mapTo(cut.parent.foldingPoly)
 
1182
                if cut.dihedralAngle()>0:
 
1183
                        foldType="valley"
 
1184
                else:
 
1185
                        foldType="mountain"
 
1186
                atts={}
 
1187
                atts["x1"] = str(edge.v1.x)
 
1188
                atts["y1"] = str(edge.v1.y)
 
1189
                atts["x2"] = str(edge.v2.x)
 
1190
                atts["y2"] = str(edge.v2.y)
 
1191
                atts["id"] = "cut"+str(cut.getID())
 
1192
                atts["class"] = foldType
 
1193
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1194
                self.e.startElement("line", a)
 
1195
                self.e.endElement("line")
 
1196
        def addPoly(self, fold):
 
1197
                face = fold.foldingPoly
 
1198
                atts = {}
 
1199
                if fold.desFace.col:
 
1200
                        col = fold.desFace.col[0]
 
1201
                        rgb = "rgb("+str(col.r)+","+str(col.g)+","+str(col.b)+")"
 
1202
                        atts["fill"] = rgb
 
1203
                atts["class"] = "poly"
 
1204
                atts["id"] = "poly"+str(face.getID())
 
1205
                points = ""
 
1206
                first = True
 
1207
                for vv in face.v:
 
1208
                        if(not(first)):
 
1209
                                points+=','
 
1210
                        first = False
 
1211
                        points+=str(vv[0])
 
1212
                        points+=' '
 
1213
                        points+=str(vv[1])
 
1214
                atts["points"] = points
 
1215
                a = xml.sax.xmlreader.AttributesImpl(atts)
 
1216
                self.e.startElement("polygon", a)
 
1217
                self.e.endElement("polygon")
 
1218
        def fileSelected(filename):
 
1219
                try:
 
1220
                        net = Registry.GetKey('unfolder')['net']
 
1221
                        exporter = SVGExporter(net, filename)
 
1222
                        exporter.export()
 
1223
                except:
 
1224
                        print "Problem exporting SVG"
 
1225
                        traceback.print_exc(file=sys.stdout)
 
1226
        fileSelected = staticmethod(fileSelected)       
 
1227
 
 
1228
 
 
1229
class NetHandler(xml.sax.handler.ContentHandler):
 
1230
        def __init__(self, net):
 
1231
                self.net = net
 
1232
                self.first = (41==41)
 
1233
                self.currentElement = None
 
1234
                self.chars = None
 
1235
                self.currentAction = None
 
1236
                self.foldsPending = {}
 
1237
                self.polys = {}
 
1238
                self.actions = {}
 
1239
                self.actions["nets:fold"] = self.foldInfo
 
1240
                self.actions["line"] = self.cutOrFold
 
1241
                self.actions["polygon"] = self.createPoly
 
1242
        def setDocumentLocator(self, locator):
 
1243
                pass
 
1244
        def startDocument(self):
 
1245
                pass
 
1246
        def endDocument(self):
 
1247
                for fold in self.foldsPending.values():
 
1248
                        face = self.net.addFace(fold.unfoldedFace())
 
1249
                        fold.desFace = face
 
1250
                        self.net.folds.append(fold)
 
1251
                self.net.addFace(self.first)
 
1252
                self.foldsPending = None
 
1253
                self.polys = None
 
1254
        def startPrefixMapping(self, prefix, uri):
 
1255
                pass
 
1256
        def endPrefixMapping(self, prefix):
 
1257
                pass
 
1258
        def startElement(self, name, attributes):
 
1259
                self.currentAction = None
 
1260
                try:
 
1261
                        self.currentAction = self.actions[name]
 
1262
                except:
 
1263
                        pass
 
1264
                if(self.currentAction!=None):
 
1265
                        self.currentAction(attributes)
 
1266
        def endElement(self, name):
 
1267
                pass
 
1268
        def startElementNS(self, name, qname, attrs):
 
1269
                self.currentAction = self.actions[name]
 
1270
                if(self.currentAction!=None):
 
1271
                        self.currentAction(attributes)
 
1272
        def endElementNS(self, name, qname):
 
1273
                pass
 
1274
        def characters(self, content):
 
1275
                pass
 
1276
        def ignorableWhitespace(self):
 
1277
                pass
 
1278
        def processingInstruction(self, target, data):
 
1279
                pass
 
1280
        def skippedEntity(self, name):
 
1281
                pass
 
1282
        def foldInfo(self, atts):
 
1283
                self.foldsPending[atts["nets:id"]] = atts
 
1284
        def createPoly(self, atts):
 
1285
                xy = re.split('[, ]' , atts["points"])
 
1286
                vectors = []
 
1287
                for i in xrange(0, len(xy)-1, 2):
 
1288
                        v = Vector([float(xy[i]), float(xy[i+1]), 0.0])
 
1289
                        vectors.append(v)
 
1290
                poly = Poly.fromVectors(vectors)
 
1291
                if(self.first==True):
 
1292
                        self.first = poly
 
1293
                self.polys[atts["id"]] = poly
 
1294
        def cutOrFold(self, atts):
 
1295
                fid = atts["id"]
 
1296
                try:
 
1297
                        fi = self.foldsPending[fid]
 
1298
                except:
 
1299
                        pass
 
1300
                p1 = Vector([float(atts["x1"]), float(atts["y1"]), 0.0])
 
1301
                p2 = Vector([float(atts["x2"]), float(atts["y2"]), 0.0])
 
1302
                edge = Edge(p1, p2)
 
1303
                parent = None
 
1304
                ofPoly = None
 
1305
                toPoly = None
 
1306
                try: 
 
1307
                        parent = self.foldsPending[fi["nets:parent"]]
 
1308
                except:
 
1309
                        pass
 
1310
                try:
 
1311
                        ofPoly = self.polys[fi["nets:ofPoly"]]
 
1312
                except:
 
1313
                        pass
 
1314
                try:
 
1315
                        toPoly = self.polys[fi["nets:toPoly"]]
 
1316
                except:
 
1317
                        pass
 
1318
                fold = Fold(parent, ofPoly , toPoly, edge, float(fi["nets:da"]))
 
1319
                self.foldsPending[fid] = fold
 
1320
        def fileSelected(filename):
 
1321
                try:
 
1322
                        net = Net.importNet(filename)
 
1323
                        try:
 
1324
                                Registry.GetKey('unfolder')['net'] = net
 
1325
                        except:
 
1326
                                Registry.SetKey('unfolder', {})
 
1327
                                Registry.GetKey('unfolder')['net'] = net
 
1328
                        Registry.GetKey('unfolder')['lastpath'] = filename
 
1329
                except:
 
1330
                        print "Problem importing SVG"
 
1331
                        traceback.print_exc(file=sys.stdout)
 
1332
        fileSelected = staticmethod(fileSelected)               
 
1333
 
 
1334
 
 
1335
class GUI:
 
1336
        def __init__(self):
 
1337
                self.overlaps = Draw.Create(0)
 
1338
                self.ani = Draw.Create(0)
 
1339
                self.selectedFaces =0
 
1340
                self.search = Draw.Create(0)
 
1341
                self.diffuse = True
 
1342
                self.ancestors = Draw.Create(0)
 
1343
                self.noise = Draw.Create(0.0)
 
1344
                self.shape = Draw.Create(0)
 
1345
                self.nOverlaps = 1==2
 
1346
                self.iterators = [RandomEdgeIterator,Brightest,Curvature,EdgeIterator,OddEven,Largest]
 
1347
                self.iterator = RandomEdgeIterator
 
1348
                self.overlapsText = "*"
 
1349
                self.message = " "
 
1350
        def makePopupGUI(self):
 
1351
                useRandom = Draw.Create(0)
 
1352
                pub = []
 
1353
                pub.append(("Search", self.search, "Search for non-overlapping net (maybe forever)"))
 
1354
                pub.append(("Random", useRandom, "Random style net"))
 
1355
                ok = True
 
1356
                while ok:
 
1357
                        ok = Blender.Draw.PupBlock("Unfold", pub)
 
1358
                        if ok:
 
1359
                                if useRandom.val:
 
1360
                                        self.iterator = RandomEdgeIterator
 
1361
                                else:
 
1362
                                        self.iterator = Curvature
 
1363
                                self.unfold()
 
1364
        def makeStandardGUI(self):
 
1365
                Draw.Register(self.draw, self.keyOrMouseEvent, self.buttonEvent)
 
1366
        def installScriptLink(self):
 
1367
                print "Adding script link for animation"
 
1368
                s = Blender.Scene.GetCurrent().getScriptLinks("FrameChanged")
 
1369
                if(s!=None and s.count("frameChanged.py")>0):
 
1370
                        return
 
1371
                try:
 
1372
                        script = Blender.Text.Get("frameChanged.py")
 
1373
                except:
 
1374
                        script = Blender.Text.New("frameChanged.py")
 
1375
                        script.write("import Blender\n")
 
1376
                        script.write("import mesh_unfolder as Unfolder\n")
 
1377
                        script.write("u = Blender.Registry.GetKey('unfolder')\n")
 
1378
                        script.write("if u!=None:\n")
 
1379
                        script.write("\tn = u['net']\n")
 
1380
                        script.write("\tif(n!=None and n.animates):\n")
 
1381
                        script.write("\t\tn.unfoldToCurrentFrame()\n")
 
1382
                Blender.Scene.GetCurrent().addScriptLink("frameChanged.py", "FrameChanged")
 
1383
        def unfold(self):
 
1384
                anc = self.ancestors.val
 
1385
                n = 0.0
 
1386
                s = True
 
1387
                self.nOverlaps = 0
 
1388
                searchLimit = 10
 
1389
                search = 1
 
1390
                Draw.Redraw(1)
 
1391
                net = None
 
1392
                name = None
 
1393
                try:
 
1394
                        self.say("Unfolding...")
 
1395
                        Draw.Redraw(1)
 
1396
                        while(s):# and search < searchLimit):
 
1397
                                if(net!=None):
 
1398
                                        name = net.des.name
 
1399
                                net = Net.unfoldSelected(self, name)
 
1400
                                net.setAvoidsOverlaps(not(self.overlaps.val))
 
1401
                                print
 
1402
                                print "Unfolding selected object"
 
1403
                                net.edgeIteratorClass = self.iterator
 
1404
                                print "Using ", net.edgeIteratorClass
 
1405
                                net.animates = self.ani.val
 
1406
                                self.diffuse = (self.ancestors.val==0)
 
1407
                                net.diffuse = self.diffuse
 
1408
                                net.generations = self.ancestors.val
 
1409
                                net.noise = self.noise.val
 
1410
                                print "even:", net.diffuse, " depth:", net.generations
 
1411
                                net.unfold()
 
1412
                                n = net.report()
 
1413
                                t = "."
 
1414
                                if(n<1.0):
 
1415
                                        t = "Overlaps>="+str(n)
 
1416
                                else:
 
1417
                                        t = "A complete net."
 
1418
                                self.nOverlaps = (n>=1)
 
1419
                                if(self.nOverlaps):
 
1420
                                        self.say(self.message+" - unfolding failed - try again ")
 
1421
                                elif(not(self.overlaps.val)):
 
1422
                                        self.say("Success. Complete net - no overlaps ")
 
1423
                                else:
 
1424
                                        self.say("Unfolding complete")
 
1425
                                self.ancestors.val = anc
 
1426
                                s = (self.search.val and n>=1.0)
 
1427
                                dict = Registry.GetKey('unfolder')
 
1428
                                if(not(dict)):
 
1429
                                        dict = {}
 
1430
                                dict['net'] = net
 
1431
                                Registry.SetKey('unfolder', dict)
 
1432
                                if(s):
 
1433
                                        net = net.clone()
 
1434
                                search += 1
 
1435
                except(IndexError):
 
1436
                        self.say("Please select an object to unfold")
 
1437
                except:
 
1438
                        self.say("Problem unfolding selected object - see console for details")
 
1439
                        print "Problem unfolding selected object:"
 
1440
                        print sys.exc_info()[1]
 
1441
                        traceback.print_exc(file=sys.stdout)
 
1442
                if(self.ani):
 
1443
                        if Registry.GetKey('unfolder')==None:
 
1444
                                print "no net!"
 
1445
                                return
 
1446
                        Registry.GetKey('unfolder')['net'].sortOutIPOSource()
 
1447
                        self.installScriptLink()
 
1448
                Draw.Redraw(1)
 
1449
        def keyOrMouseEvent(self, evt, val):
 
1450
                if (evt == Draw.ESCKEY and not val):
 
1451
                        Draw.Exit()
 
1452
        def buttonEvent(self, evt):
 
1453
                if (evt == 1):
 
1454
                        self.unfold()
 
1455
                if (evt == 5):
 
1456
                        try:
 
1457
                                Registry.GetKey('unfolder')['net'].setAvoidsOverlaps(self.overlaps.val)
 
1458
                        except:
 
1459
                                pass
 
1460
                if (evt == 2):
 
1461
                        print "Trying to set IPO curve"
 
1462
                        try:
 
1463
                                s = Blender.Object.GetSelected()
 
1464
                                if(s!=None):
 
1465
                                        Registry.GetKey('unfolder')['net'].setIPOSource( s[0] )
 
1466
                                        print "Set IPO curve"
 
1467
                                else:
 
1468
                                        print "Please select an object to use the IPO of"
 
1469
                        except:
 
1470
                                print "Problem setting IPO source"
 
1471
                        Draw.Redraw(1)
 
1472
                if (evt == 6):
 
1473
                        Draw.Exit()
 
1474
                if (evt == 7):
 
1475
                        try:
 
1476
                                if (Registry.GetKey('unfolder')['net']!=None):
 
1477
                                        Registry.GetKey('unfolder')['net'].animates = self.ani.val
 
1478
                                        if(self.ani):
 
1479
                                                Registry.GetKey('unfolder')['net'].sortOutIPOSource()
 
1480
                                                self.installScriptLink()
 
1481
                        except:
 
1482
                                print sys.exc_info()[1]
 
1483
                                traceback.print_exc(file=sys.stdout)
 
1484
                        Draw.Redraw(1)
 
1485
                if (evt == 19):
 
1486
                        pass
 
1487
                if (evt == 87):
 
1488
                        try:
 
1489
                                if (Registry.GetKey('unfolder')['net']!=None):
 
1490
                                        Registry.GetKey('unfolder')['net'].assignUVs()
 
1491
                                        self.say("Assigned UVs")
 
1492
                        except:
 
1493
                                print sys.exc_info()[1]
 
1494
                                traceback.print_exc(file=sys.stdout)
 
1495
                        Draw.Redraw(1)
 
1496
                if(evt==91):
 
1497
                        if( testOverlap() == True):
 
1498
                                self.nOverlaps = 1
 
1499
                        else:
 
1500
                                self.nOverlaps = 0
 
1501
                        Draw.Redraw(1)
 
1502
                if(evt==714):
 
1503
                        Net.unfoldAll(self)
 
1504
                        Draw.Redraw(1)
 
1505
                if(evt==713):
 
1506
                        self.iterator = self.iterators[self.shape.val]
 
1507
                        Draw.Redraw(1)
 
1508
                if(evt==92):
 
1509
                        if( testContains() == True):
 
1510
                                self.nOverlaps = 1
 
1511
                        else:
 
1512
                                self.nOverlaps = 0
 
1513
                        Draw.Redraw(1)
 
1514
                if(evt==104):
 
1515
                        try:
 
1516
                                filename = "net.svg"
 
1517
                                s = Blender.Object.GetSelected()
 
1518
                                if(s!=None and len(s)>0):
 
1519
                                        filename = s[0].getName()+".svg"
 
1520
                                else:
 
1521
                                        if (Registry.GetKey('unfolder')['net']!=None):
 
1522
                                                filename = Registry.GetKey('unfolder')['net'].des.name
 
1523
                                                if(filename==None):
 
1524
                                                        filename="net.svg"
 
1525
                                                else:
 
1526
                                                        filename=filename+".svg"
 
1527
                                Window.FileSelector(SVGExporter.fileSelected, "Select filename", filename)
 
1528
                        except:
 
1529
                                print "Problem exporting SVG"
 
1530
                                traceback.print_exc(file=sys.stdout)
 
1531
                if(evt==107):
 
1532
                        try:
 
1533
                                Window.FileSelector(NetHandler.fileSelected, "Select file")
 
1534
                        except:
 
1535
                                print "Problem importing SVG"
 
1536
                                traceback.print_exc(file=sys.stdout)
 
1537
        def say(self, m):
 
1538
                self.message = m
 
1539
                Draw.Redraw(1)
 
1540
                Window.Redraw(Window.Types.SCRIPT)
 
1541
        def draw(self):
 
1542
                cw = 64
 
1543
                ch = 16
 
1544
                l = FlowLayout(32, cw, ch, 350, 64)
 
1545
                l.y = 70
 
1546
                self.search = Draw.Toggle("search",     19,   l.nx(), l.ny(), l.cw, l.ch, self.search.val, "Search for non-overlapping mesh (potentially indefinitely)")
 
1547
                self.overlaps = Draw.Toggle("overlaps",   5,   l.nx(), l.ny(), l.cw, l.ch, self.overlaps.val, "Allow overlaps / avoid overlaps - if off, will not place overlapping faces")
 
1548
                self.ani = Draw.Toggle("ani",       7,   l.nx(), l.ny(), l.cw, l.ch, self.ani.val, "Animate net")
 
1549
                Draw.Button("uv",               87,   l.nx(), l.ny(), l.cw, l.ch, "Assign net as UV to source mesh (overwriting existing UV)")
 
1550
                Draw.Button("Unfold",           1, l.nx(), l.ny(), l.cw, l.ch, "Unfold selected mesh to net")
 
1551
                Draw.Button("save",             104,   l.nx(), l.ny(), l.cw, l.ch,  "Save net as SVG")
 
1552
                Draw.Button("load",             107,   l.nx(), l.ny(), l.cw, l.ch,  "Load net from SVG")
 
1553
                # unfolding enthusiasts - try uncommenting this
 
1554
                self.ancestors = Draw.Number("depth", 654,        l.nx(), l.ny(), cw, ch, self.ancestors.val, 0, 9999,  "depth of branching 0=diffuse")
 
1555
                #self.noise = Draw.Number("noise", 631,        l.nx(), l.ny(), cw, ch, self.noise.val, 0.0, 1.0,  "noisyness of branching")
 
1556
                #Draw.Button("UnfoldAll",           714, l.nx(), l.ny(), l.cw, l.ch, "Unfold all meshes and save their nets")
 
1557
                options = "order %t|random %x0|brightest %x1|curvature %x2|winding %x3| 1010 %x4|largest %x5"
 
1558
                self.shape = Draw.Menu(options, 713,  l.nx(), l.ny(), cw, ch, self.shape.val, "shape of net")
 
1559
                Draw.Button("exit",         6,   l.nx(), l.ny(), l.cw, l.ch, "exit")
 
1560
                BGL.glClearColor(0.5, 0.5, 0.5, 1)
 
1561
                BGL.glColor3f(0.3,0.3,0.3)
 
1562
                l.newLine()
 
1563
                BGL.glRasterPos2i(32, 100)
 
1564
                Draw.Text(self.message)
 
1565
 
 
1566
class FlowLayout:
 
1567
        def __init__(self, margin, cw, ch, w, h):
 
1568
                self.x = margin-cw-4
 
1569
                self.y = margin
 
1570
                self.cw = cw
 
1571
                self.ch = ch
 
1572
                self.width = w
 
1573
                self.height = h
 
1574
                self.margin = margin
 
1575
        def nx(self):
 
1576
                self.x+=(self.cw+4)
 
1577
                if(self.x>self.width):
 
1578
                        self.x = self.margin
 
1579
                        self.y-=self.ch+4
 
1580
                return self.x
 
1581
        def ny(self):
 
1582
                return self.y
 
1583
        def newLine(self):
 
1584
                self.y-=self.ch+self.margin
 
1585
                self.x = self.margin
 
1586
 
 
1587
try:
 
1588
        sys.setrecursionlimit(10000)
 
1589
        gui = GUI()
 
1590
        gui.makeStandardGUI()
 
1591
        #gui.makePopupGUI()
 
1592
except:
 
1593
        traceback.print_exc(file=sys.stdout)