excellent mesh unolder script by Matthew Chadwick
authorCampbell Barton <ideasman42@gmail.com>
Sun, 18 Mar 2007 12:08:51 +0000 (12:08 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sun, 18 Mar 2007 12:08:51 +0000 (12:08 +0000)
http://celeriac.net/unfolder/

release/scripts/mesh_unfolder.py [new file with mode: 0644]

diff --git a/release/scripts/mesh_unfolder.py b/release/scripts/mesh_unfolder.py
new file mode 100644 (file)
index 0000000..f725da3
--- /dev/null
@@ -0,0 +1,1601 @@
+#!BPY
+"""
+Name: 'Unfold'
+Blender: 243
+Group: 'Mesh'
+Tip: 'Unfold meshes to create nets'
+Version:  v2.2.3
+Author: Matthew Chadwick
+"""
+import Blender
+from Blender import *
+from Blender.Mathutils import *
+try:
+       import sys
+       import traceback
+       import math
+       import re
+       from math import *
+       import sys
+       import random
+       from decimal import *
+       import xml.sax, xml.sax.handler, xml.sax.saxutils
+
+except:
+       print "One of the Python modules required can't be found."
+       print sys.exc_info()[1]
+       traceback.print_exc(file=sys.stdout)
+       
+__author__ = 'Matthew Chadwick'
+__version__ = '2.2.3 07032007'
+__url__ = ["http://celeriac.net/unfolder/", "blender", "blenderartist"]
+__email__ = ["post at cele[remove this text]riac.net", "scripts"]
+__bpydoc__ = """\
+
+Mesh Unfolder
+
+Unfolds the selected mesh onto a plane to form a net
+
+Not all meshes can be unfolded
+
+Meshes must be free of holes, 
+isolated edges (not part of a face), twisted quads and other rubbish.
+Nice clean triangulated meshes unfold best
+
+This program is free software; you can distribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation; version 2
+or later, currently at http://www.gnu.org/copyleft/gpl.html
+
+The idea came while I was riding a bike.
+
+"""    
+       
+
+class FacesAndEdges:
+       def __init__(self, mesh):
+               self.nfaces = 0
+               # straight from the documentation
+               self.edgeFaces = dict([(edge.key, []) for edge in mesh.edges])
+               for face in mesh.faces:
+                       face.sel = False
+                       for key in face.edge_keys:
+                                       self.edgeFaces[key].append(face)
+       def findTakenAdjacentFace(self, bface, edge):
+               return self.findAdjacentFace(bface, edge)
+       # find the first untaken (non-selected) adjacent face in the list of adjacent faces for the given edge
+       def findAdjacentFace(self, bface, edge):
+               faces = self.edgeFaces[edge.key()]
+               for i in xrange(len(faces)):
+                       if faces[i] == bface:
+                               j = (i+1) % len(faces)
+                               while(faces[j]!=bface):
+                                       if faces[j].sel == False:
+                                               return faces[j]
+                                       j = (j+1) % len(faces)
+               return None
+       def returnFace(self, face):
+               face.sel = False
+               self.nfaces-=1
+       def facesTaken(self):
+               return self.nfaces
+       def takeAdjacentFace(self, bface, edge):
+               if (edge==None):
+                       return None
+               face = self.findAdjacentFace(bface, edge)
+               if(face!=None):
+                       face.sel = True
+                       self.nfaces+=1
+                       return face
+       def takeFace(self, bface):
+               if(bface!=None):
+                       bface.sel= True
+                       self.nfaces+=1          
+       def minz(mesh):
+               return min([v.co.z for v in mesh.verts]) 
+       minz = staticmethod(minz)
+       
+       
+class IntersectionResult:
+       def __init__(self, rn, rd, v=None):
+               self.v = v
+               self.rd = rd
+               self.rn = rn
+       def intersected(self):
+               return not(not(self.v))
+       def isParallel(self):
+               return (self.rd==0)
+       def isColinear(self):
+               return (self.rn==0)
+       def intersection(self):
+               return self.v
+       
+# represents a line segment between two points [p1, p2]. the points are [x,y]
+class LineSegment:
+       def __init__(self, p):
+               self.p = p
+       def intersects(self, s):
+               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)) 
+               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))
+               # need an epsilon closeTo() here
+               if(rd<0.0000001 or rn==0.0):
+                       return IntersectionResult(rn,rd)
+               r = rn/rd
+               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
+               i = (0.0<=r and r<=1.0 and 0.0<=s and s<=1.0)
+               if not(i):
+                       return None
+               ix = self.p[0].x + r*(self.p[1].x - self.p[0].x)
+               iy = self.p[0].y + r*(self.p[1].y - self.p[0].y)
+               t = 0.0001
+               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 ):
+                       return IntersectionResult( rn, rd,Vector([ix,iy,0.0]))
+               else:
+                       return None
+               
+class LineSegments:
+       def __init__(self, face):
+               self.face = face
+       def segmentAt(self, i):
+               if(i>self.face.nPoints()-1):
+                       return None
+               if(i==self.face.nPoints()-1):
+                       j = 0
+               else:
+                       j = i+1
+               return LineSegment([ self.face.v[i], self.face.v[j] ])
+       def iterateSegments(self, something):
+               results = []
+               for i in xrange(self.face.nPoints()):
+                       results.extend(something.haveSegment(self.segmentAt(i))) 
+               return results
+       def compareSegments(self, something, segment):
+               results = []
+               for i in xrange(self.face.nPoints()):
+                       results.append(something.compareSegments([self.segmentAt(i), segment]))
+               return results
+
+class FaceOverlapTest:
+       def __init__(self, face1, face2):
+               self.faces = [face1, face2]
+               self.segments = [ LineSegments(self.faces[0]), LineSegments(self.faces[1]) ]
+       def suspectsOverlap(self):
+               tests = self.segments[0].iterateSegments(self)
+               gi = 0
+               for i in tests:
+                       if( i!=None and i.intersected() ):
+                               gi+=1
+               return gi>0
+       def haveSegment(self, segment):
+               return self.segments[1].compareSegments(self, segment)
+       def compareSegments(self, segments):
+               return segments[0].intersects(segments[1])
+
+               
+       
+# A fold
+class Fold:
+       ids = -1
+       def __init__(self, parent, refPoly, poly, edge, angle=None):
+               Fold.ids+=1
+               self.id = Fold.ids
+               self.refPoly = refPoly
+               self.poly = poly
+               self.srcFace = None
+               self.desFace = None
+               self.edge = edge
+               self.foldedEdge = edge
+               self.rm = None
+               self.parent = parent
+               self.tree = None
+               if(refPoly!=None):
+                       self.refPolyNormal = refPoly.normal()
+               self.polyNormal = poly.normal()
+               if(angle==None):
+                       self.angle = self.calculateAngle()
+                       self.foldingPoly = poly.rotated(edge, self.angle)
+               else:
+                       self.angle = angle
+                       self.foldingPoly = poly
+               self.unfoldedEdge = self.edge
+               self.unfoldedNormal = None
+               self.animAngle = self.angle
+               self.cr = None
+               self.nancestors = None
+       def reset(self):
+               self.foldingPoly = self.poly.rotated(self.edge, self.dihedralAngle())
+       def getID(self):
+               return self.id
+       def getParent(self):
+               return self.parent
+       def ancestors(self):
+               if(self.nancestors==None):
+                       self.nancestors = self.computeAncestors()
+               return self.nancestors
+       def computeAncestors(self):     
+               if(self.parent==None):
+                       return 0
+               else:
+                       return self.parent.ancestors()+1
+       def dihedralAngle(self):        
+               return self.angle
+       def unfoldTo(self, f):
+               self.animAngle = self.angle*f
+               self.foldingPoly = self.poly.rotated(self.edge, self.animAngle)
+       def calculateAngle(self):
+               sangle = Mathutils.AngleBetweenVecs(self.refPolyNormal, self.polyNormal)
+               if(sangle!=sangle):
+                       sangle=0.0
+               ncp = Mathutils.CrossVecs(self.refPolyNormal, self.polyNormal)
+               dp = Mathutils.DotVecs(ncp, self.edge.vector)
+               if(dp>0.0):
+                       return +sangle
+               else:
+                       return -sangle
+       def alignWithParent(self):
+               pass
+       def unfoldedNormal(self):
+               return self.unfoldedNormal
+       def getEdge(self):
+               return self.edge
+       def getFace(self):
+               return self.poly
+       def testFace(self):
+               return Poly.fromVectors([self.edge.v1, self.edge.v2, Vector([0,0,0])])
+       def unfoldedFace(self):
+               return self.foldingPoly
+       def unfold(self):
+               if(self.parent!=None):
+                       self.parent.foldFace(self)
+       def foldFace(self, child):
+               child.foldingPoly.rotate(self.edge, self.animAngle)             
+               if(self.parent!=None):
+                       self.parent.foldFace(child)
+                       
+class Cut(Fold):
+       pass
+                       
+# Builds folds
+class Tree:
+       def __init__(self, net, parent,fold,otherConstructor=None):
+               self.net = net
+               self.fold = fold
+               self.face = fold.srcFace
+               self.poly = Poly.fromBlenderFace(self.face)
+               self.generations = net.generations
+               self.growing = True
+               self.tooLong = False
+               self.parent = parent
+               self.grown = False
+               if not(otherConstructor):
+                       self.edges = net.edgeIteratorClass(self)
+       def goodness(self):
+               return self.edges.goodness()
+       def compare(self, other):
+               if(self.goodness() > other.goodness()):
+                       return +1
+               else:
+                       return -1
+       def isGrowing(self):
+               return self.growing
+       def beGrowing(self):
+               self.growing = True
+       def grow(self):
+               self.tooLong = self.fold.ancestors()>self.generations
+               if(self.edges.hasNext() and self.growing):
+                       edge = self.edges.next()
+                       tface = self.net.facesAndEdges.takeAdjacentFace(self.face, edge)
+                       if(tface!=None):
+                               self.branch(tface, edge)
+                       if(self.parent==None):
+                               self.grow()
+               else:
+                       self.grown = True
+       def isGrown(self):
+               return self.grown
+       def canGrow(self):
+               return (self.parent!=None and self.parent.grown)
+       def getNet(self):
+               return self.net
+       def getFold(self):
+               return self.fold
+       def getFace(self):
+               return self.face
+       def branch(self, tface, edge):
+               fold = Fold(self.fold, self.poly, Poly.fromBlenderFace(tface), edge)
+               fold.srcFace = tface
+               self.net.myFacesVisited+=1
+               tree = Tree(self.net, self, fold)
+               fold.tree = tree
+               fold.unfold()
+               overlaps = self.net.checkOverlaps(fold)
+               nc = len(overlaps)
+               self.net.overlaps+=nc
+               if(nc>0 and self.net.avoidsOverlaps):
+                       self.handleOverlap(fold, overlaps)
+               else:
+                       self.addFace(fold)
+       def handleOverlap(self, fold, overlaps):
+               self.net.facesAndEdges.returnFace(fold.srcFace)
+               self.net.myFacesVisited-=1
+               for cfold in overlaps:
+                       ttree = cfold.tree
+                       ttree.growing = True
+                       ttree.grow()
+       def addFace(self, fold):
+               ff = fold.unfoldedFace()
+               fold.desFace = self.net.addFace(ff, fold.srcFace)
+               self.net.folds.append(fold)
+               self.net.addBranch(fold.tree)
+               fold.tree.growing = not(self.tooLong)
+               if(self.net.diffuse==False):
+                       fold.tree.grow()
+
+# Nets
+class Net:
+       def __init__(self, src, des):
+               self.src = src
+               self.des = des
+               self.firstFace = None
+               self.firstPoly = None
+               self.refFold = None
+               self.edgeIteratorClass = RandomEdgeIterator
+               if(src!=None):
+                       self.srcFaces = src.faces
+                       self.facesAndEdges = FacesAndEdges(self.src)
+               self.myFacesVisited = 0
+               self.facesAdded = 0
+               self.folds = []
+               self.cuts = []
+               self.branches = []
+               self.overlaps = 0
+               self.avoidsOverlaps = True
+               self.frame = 1
+               self.ff = 180.0
+               self.firstFaceIndex = None
+               self.trees = 0
+               self.foldIPO = None
+               self.perFoldIPO = None
+               self.IPOCurves = {}
+               self.generations = 128
+               self.diffuse = True
+               self.noise = 0.0
+               self.grownBranches = 0
+               self.assignsUV = True
+               self.animates = False
+               self.showProgress = False
+               self.feedback = None
+       def setSelectedFaces(self, faces):
+               self.srcFaces = faces
+               self.facesAndEdges = FacesAndEdges(self.srcFaces)
+       def setShowProgress(self, show):
+               self.showProgress = show
+       # this method really needs work
+       def unfold(self):
+               selectedFaces = [face for face in self.src.faces if (self.src.faceUV and face.flag & Mesh.FaceFlags.SELECT)]
+               if(self.avoidsOverlaps):
+                       print "unfolding with overlap detection"
+               if(self.firstFaceIndex==None):
+                       self.firstFaceIndex = random.randint(0, len(self.src.faces)-1)
+               else:
+                       print "Using user-selected seed face ", self.firstFaceIndex
+               self.firstFace = self.src.faces[self.firstFaceIndex]
+               z = FacesAndEdges.minz(self.src)-0.1
+               ff = Poly.fromBlenderFace(self.firstFace)
+               if(len(ff.v)<3):
+                       raise Exception("This mesh contains an isolated edge - it must consist of only faces")
+               testFace = Poly.fromVectors( [ Vector([0.0,0.0,0.0]), Vector([0.0,1.0,0.0]), Vector([1.0,1.0,0.0])  ] )
+               # hmmm
+               u=0
+               v=1
+               w=2
+               if ff.v[u].x==ff.v[u+1].x and ff.v[u].y==ff.v[u+1].y:
+                       u=1
+                       v=2
+                       w=0
+               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] ] )
+               refFace = Poly.fromVectors([ ff.v[u], ff.v[v], xyFace.v[1], xyFace.v[0] ] )
+               xyFold =  Fold(None,   xyFace, refFace, Edge(xyFace.v[0], xyFace.v[1] ))
+               self.refFold = Fold(xyFold, refFace, ff,         Edge(refFace.v[0], refFace.v[1] ))
+               self.refFold.srcFace = self.firstFace
+               trunk = Tree(self, None, self.refFold)
+               trunk.generations = self.generations
+               self.firstPoly = ff
+               self.facesAndEdges.takeFace(self.firstFace)
+               self.myFacesVisited+=1
+               self.refFold.unfold()
+               # All of his geese are swans
+               self.refFold.tree = trunk
+               self.refFold.desFace = self.addFace(self.refFold.unfoldedFace(), self.refFold.srcFace)
+               self.folds.append(self.refFold)
+               trunk.grow()
+               i = 0
+               while(self.myFacesVisited<len(self.src.faces) and len(self.branches) > 0):
+                       if self.edgeIteratorClass==RandomEdgeIterator:
+                               i = random.randint(0,len(self.branches)-1)
+                       tree = self.branches[i]
+                       if(tree.isGrown()):
+                               self.branches.pop(i)
+                       else:
+                               tree.beGrowing()
+                               if(tree.canGrow()):
+                                       tree.grow()
+                                       i = 0
+                               else:
+                                       i = (i + 1) % len(self.branches)
+               try:
+                       for face in self.src.faces:
+                               face.flag = 0   
+                       for face in selectedFaces:
+                               face.flag = Mesh.FaceFlags.SELECT
+               except:
+                       pass
+               self.src.update()
+               Window.RedrawAll()
+       def assignUVs(self):
+               for fold in self.folds:
+                       self.assignUV(fold.srcFace, fold.unfoldedFace())
+               print " assigned uv to ", len(self.folds), len(self.src.faces)
+               self.src.update()
+       def checkOverlaps(self, fold):
+               #return self.getOverlapsBetween(fold, self.folds)
+               return self.getOverlapsBetweenGL(fold, self.folds)
+       def getOverlapsBetween(self, fold, folds):
+               if(fold.parent==None):
+                       return []
+               mf = fold.unfoldedFace()
+               c = []
+               for afold in folds:
+                       mdf = afold.unfoldedFace()
+                       if(afold!=fold):
+                               it1 = FaceOverlapTest(mf, mdf)
+                               it2 = FaceOverlapTest(mdf, mf)
+                               overlap = (it1.suspectsOverlap() or it2.suspectsOverlap())
+                               inside = ( mdf.containsAnyOf(mf) or mf.containsAnyOf(mdf) )
+                               if(  overlap or inside or mdf.overlays(mf)):
+                                       c.append(afold)
+               return c
+       def getOverlapsBetweenGL(self, fold, folds):
+               b = fold.unfoldedFace().bounds()
+               polys = len(folds)*4+16 # the buffer is nhits, mindepth, maxdepth, name
+               buffer = BGL.Buffer(BGL.GL_INT, polys)
+               BGL.glSelectBuffer(polys, buffer)
+               BGL.glRenderMode(BGL.GL_SELECT)
+               BGL.glInitNames()
+               BGL.glPushName(0)
+               BGL.glPushMatrix()
+               BGL.glMatrixMode(BGL.GL_PROJECTION)
+               BGL.glLoadIdentity()
+               BGL.glOrtho(b[0].x, b[1].x, b[1].y, b[0].y, 0.0, 10.0)
+               #clip = BGL.Buffer(BGL.GL_FLOAT, 4)
+               #clip.list = [0,0,0,0]
+               #BGL.glClipPlane(BGL.GL_CLIP_PLANE1, clip)
+               # could use clipping planes here too
+               BGL.glMatrixMode(BGL.GL_MODELVIEW)
+               BGL.glLoadIdentity()
+               bx = (b[1].x - b[0].x)
+               by = (b[1].y - b[0].y)
+               cx = bx / 2.0
+               cy = by / 2.0
+               for f in xrange(len(folds)):
+                       afold = folds[f]
+                       if(fold!=afold):
+                               BGL.glLoadName(f)
+                               BGL.glBegin(BGL.GL_LINE_LOOP)
+                               for v in afold.unfoldedFace().v:
+                                       BGL.glVertex2f(v.x, v.y)
+                               BGL.glEnd()
+               BGL.glPopMatrix()
+               BGL.glFlush()
+               hits = BGL.glRenderMode(BGL.GL_RENDER)
+               buffer = [buffer[i] for i in xrange(3, 4*hits, 4)]
+               o = [folds[buffer[i]] for i in xrange(len(buffer))]
+               return self.getOverlapsBetween(fold, o)
+       def colourFace(self, face, cr):
+               for c in face.col:
+                       c.r = int(cr[0])
+                       c.g = int(cr[1])
+                       c.b = int(cr[2])
+                       c.a = int(cr[3])
+               self.src.update()
+       def setAvoidsOverlaps(self, avoids):
+               self.avoidsOverlaps = avoids
+       def addBranch(self, branch):
+               self.branches.append(branch)
+               if self.edgeIteratorClass!=RandomEdgeIterator:
+                       self.branches.sort(lambda b1, b2: b1.compare(b2))
+       def srcSize(self):
+               return len(self.src.faces)
+       def nBranches(self):
+               return len(self.branches)
+       def facesCreated(self):
+               return len(self.des.faces)
+       def facesVisited(self):
+               return self.myFacesVisited
+       def getOverlaps(self):
+               return self.overlaps
+       def sortOutIPOSource(self):
+               print "Sorting out IPO"
+               if self.foldIPO!=None:
+                       return
+               o = None
+               try:
+                       o = Blender.Object.Get("FoldRate")
+               except:
+                       o = Blender.Object.New("Empty", "FoldRate")
+                       Blender.Scene.GetCurrent().objects.link(o)
+               if(o.getIpo()==None):
+                       ipo = Blender.Ipo.New("Object", "FoldRateIPO")
+                       z = ipo.addCurve("RotZ")
+                       print " added RotZ IPO curve"
+                       z.addBezier((1,0))
+                       # again, why is this 10x out ?
+                       z.addBezier((180, self.ff/10.0))
+                       z.addBezier((361, 0.0))
+                       o.setIpo(ipo)
+                       z.recalc()
+                       z.setInterpolation("Bezier")
+                       z.setExtrapolation("Cyclic")
+               self.setIPOSource(o)
+               print " added IPO source"
+       def setIPOSource(self, object):
+               try:
+                       self.foldIPO = object
+                       for i in xrange(self.foldIPO.getIpo().getNcurves()):
+                               self.IPOCurves[self.foldIPO.getIpo().getCurves()[i].getName()] = i
+                               print " added ", self.foldIPO.getIpo().getCurves()[i].getName()
+               except:
+                       print "Problem setting IPO object"
+                       print sys.exc_info()[1]
+                       traceback.print_exc(file=sys.stdout)
+       def setFoldFactor(self, ff):
+               self.ff = ff
+       def sayTree(self):
+               for fold in self.folds:
+                       if(fold.getParent()!=None):
+                               print fold.getID(), fold.dihedralAngle(), fold.getParent().getID()
+       def report(self):
+               p = int(float(self.myFacesVisited)/float(len(self.src.faces)) * 100)
+               print str(p) + "% unfolded"
+               print "faces created:", self.facesCreated()
+               print "faces visited:", self.facesVisited()
+               print "originalfaces:", len(self.src.faces)
+               n=0
+               if(self.avoidsOverlaps):
+                       print "net avoided at least ", self.getOverlaps(), " overlaps ",
+                       n = len(self.src.faces) - self.facesCreated()
+                       if(n>0):
+                               print "but was unable to avoid ", n, " overlaps. Incomplete net."
+                       else:
+                               print "- A complete net."
+               else:
+                       print "net has at least ", self.getOverlaps(), " collision(s)"
+               return n
+       # fold all my folds to a fraction of their total fold angle
+       def unfoldToCurrentFrame(self):
+               self.unfoldTo(Blender.Scene.GetCurrent().getRenderingContext().currentFrame())
+       def unfoldTo(self, frame):
+               frames = Blender.Scene.GetCurrent().getRenderingContext().endFrame()
+               if(self.foldIPO!=None and self.foldIPO.getIpo()!=None):
+                       f = self.foldIPO.getIpo().EvaluateCurveOn(self.IPOCurves["RotZ"],frame)
+                       # err, this number seems to be 10x less than it ought to be
+                       fff = 1.0 - (f*10.0 / self.ff)
+               else:
+                       fff = 1.0-((frame)/(frames*1.0))
+               for fold in self.folds:
+                       fold.unfoldTo(fff)
+               for fold in self.folds:
+                       fold.unfold()
+                       tface = fold.unfoldedFace()
+                       bface = fold.desFace
+                       i = 0
+                       for v in bface.verts:
+                               v.co.x = tface.v[i].x
+                               v.co.y = tface.v[i].y
+                               v.co.z = tface.v[i].z
+                               i+=1
+               Window.Redraw(Window.Types.VIEW3D)
+               return None
+       def addFace(self, poly, originalFace=None):
+               originalLength = len(self.des.verts)
+               self.des.verts.extend([Vector(vv.x, vv.y, vv.z) for vv in poly.v])
+               self.des.faces.extend([ range(originalLength, originalLength + poly.size()) ])
+               newFace = self.des.faces[len(self.des.faces)-1]
+               newFace.uv = [vv for vv in poly.v]
+               if(originalFace!=None and self.src.vertexColors):
+                       newFace.col = [c for c in originalFace.col]
+               if(self.feedback!=None):
+                       pu = str(int(self.fractionUnfolded() * 100))+"% unfolded"
+                       howMuchDone = str(self.myFacesVisited)+" of "+str(len(self.src.faces))+"  "+pu
+                       self.feedback.say(howMuchDone)
+                       #Window.DrawProgressBar (p, pu)
+               if(self.showProgress):
+                       Window.Redraw(Window.Types.VIEW3D)
+               return newFace
+       def fractionUnfolded(self):
+               return float(self.myFacesVisited)/float(len(self.src.faces))
+       def assignUV(self, face, uv):
+               face.uv = [Vector(v.x, v.y) for v in uv.v]
+       def unfoldAll(feedback=None):
+               objects = Blender.Object.Get()
+               for object in objects:
+                       if(object.getType()=='Mesh' and not(object.getName().endswith("_net")) and len(object.getData(False, True).faces)>1):
+                               net = Net.createNet(object, feedback)
+                               net.searchForUnfolding()
+                               svg = SVGExporter(net, object.getName()+".svg")
+                               svg.export()
+       unfoldAll = staticmethod(unfoldAll)
+       def searchForUnfolding(self, limit=-1):
+               overlaps = 1
+               attempts = 0
+               while(overlaps > 0 or attempts<limit):
+                       self.unfold()
+                       overlaps = self.report()
+                       attempts+=1
+               return attempts
+       def unfoldSelected(feedback=None, netName=None):
+               return Net.createNet(Blender.Object.GetSelected()[0], feedback, netName)
+       unfoldSelected = staticmethod(unfoldSelected)
+       def clone(self, object=None):
+               if(object==None):
+                       object = self.object
+               net = Net.createNet(object, self.feedback)
+               net.avoidsOverlaps = net.avoidsOverlaps
+               return net
+       def createNet(ob, feedback=None, netName=None):
+               mesh = ob.getData(mesh=1)
+               netObject = None
+               if(netName==None):
+                       netName = ob.name[0:16]+"_net"
+               try:
+                       netObject = Blender.Object.Get(netName)
+                       netMesh = netObject.getData(False, True)
+                       if(netMesh!=None):
+                               netMesh.verts = None # clear the mesh
+                       else:
+                               netObject = Blender.Object.New("Mesh", netName)
+               except:
+                       if(netObject==None):
+                               netObject = Blender.Object.New("Mesh", netName)
+               netMesh = netObject.getData(False, True)  # True means "as a Mesh not an NMesh"
+               try:
+                       Blender.Scene.GetCurrent().objects.link(netObject)
+               except:
+                       pass
+               try:
+                       netMesh.materials = mesh.materials
+                       netMesh.vertexColors = True
+               except:
+                       print "Problem setting materials here"
+               net = Net(mesh, netMesh)
+               if(mesh.faceUV and mesh.activeFace>=0 and (mesh.faces[mesh.activeFace].flag & Mesh.FaceFlags.SELECT)):
+                       net.firstFaceIndex = mesh.activeFace
+               net.object = ob
+               net.feedback = feedback
+               return net
+       createNet = staticmethod(createNet)
+       def importNet(filename):
+               netName = filename.rstrip(".svg").replace("\\","/")
+               netName = netName[netName.rfind("/")+1:]
+               try:
+                       netObject = Blender.Object.Get(netName)
+               except:
+                       netObject  = Blender.Object.New("Mesh", netName)
+               netObject.getData(mesh=1).name = netName
+               try:
+                       Blender.Scene.GetCurrent().objects.link(netObject)
+               except:
+                       pass
+               net = Net(None, netObject.getData(mesh=1))
+               handler = NetHandler(net)
+               xml.sax.parse(filename, handler)
+               Window.Redraw(Window.Types.VIEW3D)
+               return net
+       importNet = staticmethod(importNet)
+       def getSourceMesh(self):
+               return self.src
+               
+               
+class EdgeIterator:
+       def __init__(self, branch, otherConstructor=None):
+               self.branch = branch
+               self.bface = branch.getFace()
+               self.edge = branch.getFold().getEdge()
+               self.net = branch.getNet()
+               self.n = len(self.bface)
+               self.edges = []
+               self.i = 0
+               self.gooodness = 0
+               self.createEdges()
+               self.computeGoodness()
+               if(otherConstructor==None):
+                       self.sequenceEdges()
+       def createEdges(self):
+               edge = None
+               e = Edge.edgesOfBlenderFace(self.net.getSourceMesh(), self.bface)
+               for edge in e:
+                       if not(edge.isBlenderSeam() and edge!=self.edge):
+                               self.edges.append(edge)
+       def sequenceEdges(self):
+               pass
+       def next(self):
+               edge = self.edges[self.i]
+               self.i+=1
+               return edge
+       def size(self):
+               return len(self.edges)
+       def reset(self):
+               self.i = 0
+       def hasNext(self):
+               return (self.i<len(self.edges))
+       def goodness(self):
+               return self.gooodness
+       def computeGoodness(self):
+               self.gooodness = 0
+       def rotate(self):
+               self.edges.append(self.edges.pop(0))
+
+class RandomEdgeIterator(EdgeIterator):
+       def sequenceEdges(self):
+               random.seed()
+               random.shuffle(self.edges)
+       def goodness(self):
+               return random.randint(0, self.net.srcSize())
+       
+               
+class Largest(EdgeIterator):
+       def sequenceEdges(self):
+               for e in self.edges:
+                       f = self.net.facesAndEdges.findAdjacentFace(self.bface, e)
+                       if(f!=None):
+                               e.setGoodness(f.area)
+               self.edges.sort(lambda e1, e2: -e1.compare(e2))
+       def computeGoodness(self):
+               self.gooodness = self.bface.area
+               
+                       
+class Brightest(EdgeIterator):
+       def sequenceEdges(self):
+               for edge in self.edges:
+                       f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
+                       if(f!=None):
+                               b = 0
+                               for c in f.col:
+                                       b+=(c.g+c.r+c.b)
+                               rc = float(random.randint(0, self.net.srcSize())) / float(self.net.srcSize()) / 100.0
+                               b+=rc
+                               edge.setGoodness(b)
+               self.edges.sort(lambda e1, e2: e1.compare(e2))
+       def computeGoodness(self):
+               g = 0
+               for c in self.bface.col:
+                       g+=(c.g+c.r+c.b)
+               self.gooodness = g
+               
+class OddEven(EdgeIterator):
+       i = True
+       def sequenceEdges(self):
+               OddEven.i = not(OddEven.i)
+               if(OddEven.i):
+                       self.edges.reverse()
+               
+# local curvature
+class Curvature(EdgeIterator):
+       def sequenceEdges(self):
+               p1 = Poly.fromBlenderFace(self.bface)
+               gg = 0.0
+               for edge in self.edges:
+                       f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
+                       if(f!=None):
+                               p2 = Poly.fromBlenderFace(f)
+                               fold = Fold(None, p1, p2, edge)
+                               fold.srcFace = f
+                               b = Tree(self.net, self.branch, fold, self)
+                               c = Curvature(b, False)
+                               g = c.goodness()
+                               gg+=g
+                               edge.setGoodness(g)
+               self.edges.sort(lambda e1, e2: e1.compare(e2))
+               tg = (self.gooodness + gg)
+               rc = float(random.randint(0, self.net.srcSize())) / float(self.net.srcSize()) / 100.0
+               if(tg!=0.0):
+                       self.gooodness = self.gooodness + rc / tg
+       def computeGoodness(self):
+               g = 0
+               for edge in self.edges:
+                       f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
+                       if(f!=None):
+                               p1 = Poly.fromBlenderFace(self.bface)
+                               p2 = Poly.fromBlenderFace(f)
+                               f = Fold(None, p1, p2, edge)
+                               g += f.dihedralAngle()
+               self.gooodness = g
+               
+       
+class Edge:
+       def __init__(self, v1=None, v2=None, mEdge=None, i=-1):
+               self.idx = i
+               if v1 and v2: # Neither are None
+                       self.v1 = v1.copy()
+                       self.v2 = v2.copy()
+               else:
+                       self.v1 = mEdge.v1.co.copy()
+                       self.v2 = mEdge.v2.co.copy()
+               self.v1n = -self.v1
+               self.vector = self.v1-self.v2
+               self.vector.resize3D()
+               self.vector.normalize()
+               self.bmEdge = mEdge
+               self.gooodness = 0.0
+       def fromBlenderFace(mesh, bface, i):
+               if(i>len(bface)-1):
+                       return None
+               if(i==len(bface)-1):
+                       j = 0
+               else:
+                       j = i+1
+               edge =  Edge( bface.v[i].co.copy(), bface.v[j].co.copy() )
+               edge.bEdge = mesh.findEdge(bface.v[i], bface.v[j])
+               edge.idx = i
+               return edge
+       fromBlenderFace=staticmethod(fromBlenderFace)
+       def edgesOfBlenderFace(mesh, bmFace):
+               edges = [mesh.edges[mesh.findEdges(edge[0], edge[1])] for edge in bmFace.edge_keys]
+               v = bmFace.verts
+               e = []
+               vi = v[0]
+               i=0
+               for j in xrange(1, len(bmFace)+1):
+                       vj = v[j%len(bmFace)]
+                       for ee in edges:
+                               if((ee.v1.index==vi.index and ee.v2.index==vj.index) or (ee.v2.index==vi.index and ee.v1.index==vj.index)):
+                                       e.append(Edge(vi.co, vj.co, ee, i))
+                                       i+=1
+                       vi = vj
+               return e
+       edgesOfBlenderFace=staticmethod(edgesOfBlenderFace)
+       def isBlenderSeam(self):
+               # Better and flutter must and man can beam. Now think of seams.
+               return (self.bmEdge.flag & Mesh.EdgeFlags.SEAM)
+       def isInFGon(self):
+               return (self.bmEdge.flag & Mesh.EdgeFlags.FGON)
+       def mapTo(self, poly):
+               if(self.idx==len(poly.v)-1):
+                       j = 0
+               else:
+                       j = self.idx+1
+               return Edge(poly.v[self.idx], poly.v[j])
+       def isDegenerate(self):
+               return self.vector.length==0
+       def vertices(s):
+               return [ [s.v1.x, s.v1.y, s.v1.z], [s.v2.x, s.v2.y,s.v2.z] ]
+       def key(self):
+               return self.bmEdge.key
+       def goodness(self):
+               return self.gooodness
+       def setGoodness(self, g):
+               self.gooodness = g
+       def compare(self, other):
+               if(self.goodness() > other.goodness()):
+                       return +1
+               else:
+                       return -1
+       
+class Poly:
+       ids = -1
+       def __init__(self):
+               Poly.ids+=1
+               self.v = []
+               self.id = Poly.ids
+               self.boundz = None
+       def getID(self):
+               return self.id
+       def normal(self):
+               a =self.v[0]
+               b=self.v[1]
+               c=self.v[2]
+               p = b-a
+               p.resize3D()
+               q = a-c
+               q.resize3D()
+               return CrossVecs(p,q)
+       def isBad(self):
+               badness = 0
+               for vv in self.v:
+                       if(vv.x!=vv.x or vv.y!=vv.y or vv.z!=vv.z): # Nan check
+                               badness+=1
+               return (badness>0)
+       def midpoint(self):
+               x=y=z = 0.0
+               n = 0
+               for vv in self.v:
+                       x+=vv.x
+                       y+=vv.y
+                       z+=vv.z
+                       n+=1
+               return [ x/n, y/n, z/n ]
+       def centerAtOrigin(self):
+               mp = self.midpoint()
+               mp = -mp
+               toOrigin = TranslationMatrix(mp)
+               self.v = [(vv * toOrigin) for vv in self.v]
+       def move(self, tv):
+               mv = TranslationMatrix(tv)
+               self.v = [(vv * mv) for vv in self.v]
+       def scale(self, s):
+               mp = Vector(self.midpoint())
+               fromOrigin = TranslationMatrix(mp)
+               mp = -mp
+               toOrigin = TranslationMatrix(mp)
+               sm = ScaleMatrix(s, 4)
+               # Todo, the 3 lines below in 1 LC
+               self.v = [(vv * toOrigin) for vv in self.v]
+               self.v = [(sm * vv) for vv in self.v]
+               self.v = [(vv * fromOrigin) for vv in self.v]
+       def nPoints(self):
+               return len(self.v)
+       def size(self):
+               return len(self.v)
+       def rotated(self, axis, angle):
+               p = self.clone()
+               p.rotate(axis, angle)
+               return p
+       def rotate(self, axis, angle):
+               rotation = RotationMatrix(angle, 4, "r", axis.vector)
+               toOrigin = TranslationMatrix(axis.v1n)
+               fromOrigin = TranslationMatrix(axis.v1)
+               # Todo, the 3 lines below in 1 LC
+               self.v = [(vv * toOrigin) for vv in self.v]
+               self.v = [(rotation * vv) for vv in self.v]
+               self.v = [(vv * fromOrigin) for vv in self.v]
+       def moveAlong(self, vector, distance):
+               t = TranslationMatrix(vector)
+               s = ScaleMatrix(distance, 4)
+               ts = t*s
+               self.v = [(vv * ts) for vv in self.v]
+       def bounds(self):
+               if(self.boundz == None):
+                       vv = [vv for vv in self.v]
+                       vv.sort(key=lambda v: v.x)
+                       minx = vv[0].x
+                       maxx = vv[len(vv)-1].x
+                       vv.sort(key=lambda v: v.y)
+                       miny = vv[0].y
+                       maxy = vv[len(vv)-1].y
+                       self.boundz = [Vector(minx, miny, 0), Vector(maxx, maxy, 0)]
+               return self.boundz
+       def fromBlenderFace(bface):
+               p = Poly()
+               for vv in bface.v:
+                       vec = Vector([vv.co[0], vv.co[1], vv.co[2] , 1.0]) 
+                       p.v.append(vec)
+               return p
+       fromBlenderFace = staticmethod(fromBlenderFace)
+       def fromList(list):
+               p = Poly()
+               for vv in list:
+                       vec = Vector( [vvv for vvv in vv] )
+                       vec.resize4D()
+                       p.v.append(vec)
+               return p
+       fromList = staticmethod(fromList)
+       def fromVectors(vectors):
+               p = Poly()
+               p.v.extend([v.copy().resize4D() for v in vectors])
+               return p
+       fromVectors = staticmethod(fromVectors)
+       def clone(self):
+               p = Poly()
+               p.v.extend(self.v)
+               return p
+       def hasVertex(self, ttv):
+               v = Mathutils.Vector(ttv)
+               v.normalize()
+               for tv in self.v:
+                       vv = Mathutils.Vector(tv)
+                       vv.normalize()
+                       t = 0.00001
+                       if abs(vv.x-v.x)<t and abs(vv.y-v.y)<t:
+                               return True
+               return False
+       def overlays(self, poly):
+               if len(poly.v)!=len(self.v):
+                       return False
+               c = 0
+               for point in poly.v:
+                       if self.hasVertex(point):
+                               c+=1
+               return c==len(self.v)
+       def sharesVertexWith(self, poly):
+               for point in poly.v:
+                       if(self.hasVertex(point)):
+                               return True
+               return False
+       def containsAnyOf(self, poly):
+               for point in poly.v:
+                       if(not(self.hasVertex(point))):
+                               if self.contains(point):
+                                       return True
+               return False
+       def toString(self):
+               return self.v
+       # This is the BEST algorithm for point-in-polygon detection.
+       # It's by W. Randolph Franklin. It's also very beautiful (looks even better in C).
+       # All the others are shite; they give false positives.
+       # returns 1 for inside, 1 or 0 for edges
+       def contains(self, tp):
+               c = 0
+               j = len(self.v)-1
+               for i in xrange(len(self.v)):
+                       if(i>0): j=i-1
+                       cv = self.v[i]
+                       nv = self.v[j]
+                       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)):
+                               c = not(c)
+               return (c == 1)
+               
+class SVGExporter:
+       def __init__(self, net, filename):
+               self.net = net
+               print self.net.des.name
+               self.object = self.net.object
+               print "Exporting ", self.object
+               self.filename = filename
+               self.file = None
+               self.e = None
+               self.width = 1024
+               self.height = 768
+       def start(self):
+               print "Exporting SVG to ", self.filename
+               self.file = open(self.filename, 'w')
+               self.e = xml.sax.saxutils.XMLGenerator(self.file, "UTF-8")
+               atts = {}
+               atts["width"] = "100%"
+               atts["height"] = "100%"
+               atts["viewBox"] = str(self.vxmin)+" "+str(self.vymin)+" "+str(self.vxmax-self.vxmin)+" "+str(self.vymax-self.vymin)
+               atts["xmlns:nets"] = "http://celeriac.net/unfolder/rdf#"
+               atts["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
+               atts["xmlns"] ="http://www.w3.org/2000/svg"
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startDocument()
+               self.e.startElement("svg", a)
+               self.e.startElement("defs", xml.sax.xmlreader.AttributesImpl({}))
+               atts = {}
+               atts["type"]="text/css"
+               self.e.startElement("style", atts)
+               # can't find a proper way to do this
+               self.file.write("<![CDATA[")
+               self.file.write("polygon.poly{fill:white;stroke:black;stroke-width: 0.001}")
+               self.file.write("g#foldLines line.valley{stroke:white;stroke-width:0.01;stroke-dasharray:0.02,0.01,0.02,0.05}")
+               self.file.write("g#foldLines line.mountain{stroke:white;stroke-width:0.01;stroke-dasharray:0.02,0.04}")
+               self.file.write("]]>")
+               self.e.endElement("style")
+               self.e.endElement("defs")
+               #self.addClipPath()
+               self.addMeta()
+       def addMeta(self):
+               self.e.startElement("metadata", xml.sax.xmlreader.AttributesImpl({}))
+               self.e.startElement("nets:net", xml.sax.xmlreader.AttributesImpl({}))
+               for i in xrange(1, len(self.net.folds)):
+                       fold = self.net.folds[i]
+                       # AttributesNSImpl - documentation is rubbish. using this hack.
+                       atts = {}
+                       atts["nets:id"] = "fold"+str(fold.getID())
+                       if(fold.parent!=None):
+                               atts["nets:parent"] = "fold"+str(fold.parent.getID())
+                       else:
+                               atts["nets:parent"] = "null"
+                       atts["nets:da"] = str(fold.dihedralAngle())
+                       if(fold.parent!=None):
+                               atts["nets:ofPoly"] = "poly"+str(fold.parent.foldingPoly.getID())
+                       else:
+                               atts["nets:ofPoly"] = ""
+                       atts["nets:toPoly"] = "poly"+str(fold.foldingPoly.getID())
+                       a = xml.sax.xmlreader.AttributesImpl(atts)
+                       self.e.startElement("nets:fold",  a)
+                       self.e.endElement("nets:fold")
+               self.e.endElement("nets:net")
+               self.e.endElement("metadata")
+       def end(self):
+               self.e.endElement("svg")
+               self.e.endDocument()
+               print "grown."
+       def export(self):
+               self.net.unfoldTo(1)
+               bb = self.object.getBoundBox()
+               self.vxmin = bb[0][0]
+               self.vymin = bb[0][1]
+               self.vxmax = bb[7][0]
+               self.vymax = bb[7][1]
+               self.start()
+               atts = {}
+               atts["id"] = self.object.getName()
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("g", a)
+               #self.addUVImage()
+               self.addPolys()
+               self.addFoldLines()
+               #self.addCutLines()
+               self.e.endElement("g")
+               self.end()
+       def addClipPath(self):
+               atts = {}
+               atts["id"] = "netClip"
+               atts["clipPathUnits"] = "userSpaceOnUse"
+               atts["x"] = str(self.vxmin)
+               atts["y"] = str(self.vymin)
+               atts["width"] = "100%"
+               atts["height"] = "100%"
+               self.e.startElement("clipPath", atts)
+               self.addPolys()
+               self.e.endElement("clipPath")
+       def addUVImage(self):
+               image = Blender.Image.GetCurrent()
+               if image==None:
+                       return
+               ifn = image.getFilename()
+               #ifn = self.filename.replace(".svg", ".jpg")
+               #image.setFilename(ifn)
+               #ifn = ifn[ifn.rfind("/")+1:]
+               #image.save()
+               atts = {}
+               atts["clip-path"] = "url(#netClip)"
+               atts["xlink:href"] = ifn
+               self.e.startElement("image", atts)
+               self.e.endElement("image")
+       def addPolys(self):
+               atts = {}
+               atts["id"] = "polys"
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("g", a)
+               for i in xrange(len(self.net.folds)):
+                       self.addPoly(self.net.folds[i])
+               self.e.endElement("g")
+       def addFoldLines(self):
+               atts = {}
+               atts["id"] = "foldLines"
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("g", a)
+               for i in xrange( 1, len(self.net.folds)):
+                       self.addFoldLine(self.net.folds[i])
+               self.e.endElement("g")
+       def addFoldLine(self, fold):
+               edge = fold.edge.mapTo(fold.parent.foldingPoly)
+               if fold.dihedralAngle()>0:
+                       foldType="valley"
+               else:
+                       foldType="mountain"
+               atts={}
+               atts["x1"] = str(edge.v1.x)
+               atts["y1"] = str(edge.v1.y)
+               atts["x2"] = str(edge.v2.x)
+               atts["y2"] = str(edge.v2.y)
+               atts["id"] = "fold"+str(fold.getID())
+               atts["class"] = foldType
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("line", a)
+               self.e.endElement("line")
+       def addCutLines(self):
+               atts = {}
+               atts["id"] = "cutLines"
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("g", a)
+               for i in xrange( 1, len(self.net.cuts)):
+                       self.addCutLine(self.net.cuts[i])
+               self.e.endElement("g")
+       def addCutLine(self, cut):
+               edge = cut.edge.mapTo(cut.parent.foldingPoly)
+               if cut.dihedralAngle()>0:
+                       foldType="valley"
+               else:
+                       foldType="mountain"
+               atts={}
+               atts["x1"] = str(edge.v1.x)
+               atts["y1"] = str(edge.v1.y)
+               atts["x2"] = str(edge.v2.x)
+               atts["y2"] = str(edge.v2.y)
+               atts["id"] = "cut"+str(cut.getID())
+               atts["class"] = foldType
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("line", a)
+               self.e.endElement("line")
+       def addPoly(self, fold):
+               face = fold.foldingPoly
+               atts = {}
+               if fold.desFace.col:
+                       col = fold.desFace.col[0]
+                       rgb = "rgb("+str(col.r)+","+str(col.g)+","+str(col.b)+")"
+                       atts["fill"] = rgb
+               atts["class"] = "poly"
+               atts["id"] = "poly"+str(face.getID())
+               points = ""
+               first = True
+               for vv in face.v:
+                       if(not(first)):
+                               points+=','
+                       first = (2==3)
+                       points+=str(vv[0])
+                       points+=' '
+                       points+=str(vv[1])
+               atts["points"] = points
+               a = xml.sax.xmlreader.AttributesImpl(atts)
+               self.e.startElement("polygon", a)
+               self.e.endElement("polygon")
+       def fileSelected(filename):
+               try:
+                       net = Registry.GetKey('unfolder')['net']
+                       exporter = SVGExporter(net, filename)
+                       exporter.export()
+               except:
+                       print "Problem exporting SVG"
+                       traceback.print_exc(file=sys.stdout)
+       fileSelected = staticmethod(fileSelected)       
+
+
+class NetHandler(xml.sax.handler.ContentHandler):
+       def __init__(self, net):
+               self.net = net
+               self.first = (41==41)
+               self.currentElement = None
+               self.chars = None
+               self.currentAction = None
+               self.foldsPending = {}
+               self.polys = {}
+               self.actions = {}
+               self.actions["nets:fold"] = self.foldInfo
+               self.actions["line"] = self.cutOrFold
+               self.actions["polygon"] = self.createPoly
+       def setDocumentLocator(self, locator):
+               pass
+       def startDocument(self):
+               pass
+       def endDocument(self):
+               for fold in self.foldsPending.values():
+                       face = self.net.addFace(fold.unfoldedFace())
+                       fold.desFace = face
+                       self.net.folds.append(fold)
+               self.net.addFace(self.first)
+               self.foldsPending = None
+               self.polys = None
+       def startPrefixMapping(self, prefix, uri):
+               pass
+       def endPrefixMapping(self, prefix):
+               pass
+       def startElement(self, name, attributes):
+               self.currentAction = None
+               try:
+                       self.currentAction = self.actions[name]
+               except:
+                       pass
+               if(self.currentAction!=None):
+                       self.currentAction(attributes)
+       def endElement(self, name):
+               pass
+       def startElementNS(self, name, qname, attrs):
+               self.currentAction = self.actions[name]
+               if(self.currentAction!=None):
+                       self.currentAction(attributes)
+       def endElementNS(self, name, qname):
+               pass
+       def characters(self, content):
+               pass
+       def ignorableWhitespace(self):
+               pass
+       def processingInstruction(self, target, data):
+               pass
+       def skippedEntity(self, name):
+               pass
+       def foldInfo(self, atts):
+               self.foldsPending[atts["nets:id"]] = atts
+       def createPoly(self, atts):
+               xy = re.split('[, ]' , atts["points"])
+               vectors = []
+               for i in xrange(0, len(xy)-1, 2):
+                       v = Vector([float(xy[i]), float(xy[i+1]), 0.0])
+                       vectors.append(v)
+               poly = Poly.fromVectors(vectors)
+               if(self.first==True):
+                       self.first = poly
+               self.polys[atts["id"]] = poly
+       def cutOrFold(self, atts):
+               fid = atts["id"]
+               try:
+                       fi = self.foldsPending[fid]
+               except:
+                       pass
+               p1 = Vector([float(atts["x1"]), float(atts["y1"]), 0.0])
+               p2 = Vector([float(atts["x2"]), float(atts["y2"]), 0.0])
+               edge = Edge(p1, p2)
+               parent = None
+               ofPoly = None
+               toPoly = None
+               try: 
+                       parent = self.foldsPending[fi["nets:parent"]]
+               except:
+                       pass
+               try:
+                       ofPoly = self.polys[fi["nets:ofPoly"]]
+               except:
+                       pass
+               try:
+                       toPoly = self.polys[fi["nets:toPoly"]]
+               except:
+                       pass
+               fold = Fold(parent, ofPoly , toPoly, edge, float(fi["nets:da"]))
+               self.foldsPending[fid] = fold
+       def fileSelected(filename):
+               try:
+                       net = Net.importNet(filename)
+                       try:
+                               Registry.GetKey('unfolder')['net'] = net
+                       except:
+                               Registry.SetKey('unfolder', {})
+                               Registry.GetKey('unfolder')['net'] = net
+                       Registry.GetKey('unfolder')['lastpath'] = filename
+               except:
+                       print "Problem importing SVG"
+                       traceback.print_exc(file=sys.stdout)
+       fileSelected = staticmethod(fileSelected)               
+
+       
+       
+#____________Blender GUI__________________
+       
+class GUI:
+       def __init__(self):
+               self.overlaps = Draw.Create(0)
+               self.ani = Draw.Create(0)
+               self.selectedFaces =0
+               self.search = Draw.Create(0)
+               self.diffuse = True
+               self.ancestors = Draw.Create(0)
+               self.noise = Draw.Create(0.0)
+               self.shape = Draw.Create(0)
+               self.nOverlaps = 1==2
+               self.iterators = [RandomEdgeIterator,Brightest,Curvature,EdgeIterator,OddEven,Largest]
+               self.iterator = RandomEdgeIterator
+               self.overlapsText = "*"
+               self.message = " "
+       def makePopupGUI(self):
+               useRandom = Draw.Create(0)
+               pub = []
+               pub.append(("Search", self.search, "Search for non-overlapping net (maybe forever)"))
+               pub.append(("Random", useRandom, "Random style net"))
+               ok = True
+               while ok:
+                       ok = Blender.Draw.PupBlock("Unfold", pub)
+                       if ok:
+                               if useRandom.val:
+                                       self.iterator = RandomEdgeIterator
+                               else:
+                                       self.iterator = Curvature
+                               self.unfold()
+       def makeStandardGUI(self):
+               Draw.Register(self.draw, self.keyOrMouseEvent, self.buttonEvent)
+       def installScriptLink(self):
+               print "Adding script link for animation"
+               s = Blender.Scene.GetCurrent().getScriptLinks("FrameChanged")
+               if(s!=None and s.count("frameChanged.py")>0):
+                       return
+               try:
+                       script = Blender.Text.Get("frameChanged.py")
+               except:
+                       script = Blender.Text.New("frameChanged.py")
+                       script.write("import Blender\n")
+                       script.write("import mesh_unfolder as Unfolder\n")
+                       script.write("u = Blender.Registry.GetKey('unfolder')\n")
+                       script.write("if u!=None:\n")
+                       script.write("\tn = u['net']\n")
+                       script.write("\tif(n!=None and n.animates):\n")
+                       script.write("\t\tn.unfoldToCurrentFrame()\n")
+               Blender.Scene.GetCurrent().addScriptLink("frameChanged.py", "FrameChanged")
+       def unfold(self):
+               anc = self.ancestors.val
+               n = 0.0
+               s = True
+               self.nOverlaps = 0
+               searchLimit = 10
+               search = 1
+               Draw.Redraw(1)
+               net = None
+               name = None
+               try:
+                       self.say("Unfolding...")
+                       Draw.Redraw(1)
+                       while(s):# and search < searchLimit):
+                               if(net!=None):
+                                       name = net.des.name
+                               net = Net.unfoldSelected(self, name)
+                               net.setAvoidsOverlaps(not(self.overlaps.val))
+                               print
+                               print "Unfolding selected object"
+                               net.edgeIteratorClass = self.iterator
+                               print "Using ", net.edgeIteratorClass
+                               net.animates = self.ani.val
+                               self.diffuse = (self.ancestors.val==0)
+                               net.diffuse = self.diffuse
+                               net.generations = self.ancestors.val
+                               net.noise = self.noise.val
+                               print "even:", net.diffuse, " depth:", net.generations
+                               net.unfold()
+                               n = net.report()
+                               t = "."
+                               if(n<1.0):
+                                       t = "Overlaps>="+str(n)
+                               else:
+                                       t = "A complete net."
+                               self.nOverlaps = (n>=1)
+                               if(self.nOverlaps):
+                                       self.say(self.message+" - unfolding failed - try again ")
+                               elif(not(self.overlaps.val)):
+                                       self.say("Success. Complete net - no overlaps ")
+                               else:
+                                       self.say("Unfolding complete")
+                               self.ancestors.val = anc
+                               s = (self.search.val and n>=1.0)
+                               dict = Registry.GetKey('unfolder')
+                               if(not(dict)):
+                                       dict = {}
+                               dict['net'] = net
+                               Registry.SetKey('unfolder', dict)
+                               if(s):
+                                       net = net.clone()
+                               search += 1
+               except(IndexError):
+                       self.say("Please select an object to unfold")
+               except:
+                       self.say("Problem unfolding selected object - see console for details")
+                       print "Problem unfolding selected object:"
+                       print sys.exc_info()[1]
+                       traceback.print_exc(file=sys.stdout)
+               if(self.ani):
+                       if Registry.GetKey('unfolder')==None:
+                               print "no net!"
+                               return
+                       Registry.GetKey('unfolder')['net'].sortOutIPOSource()
+                       self.installScriptLink()
+               Draw.Redraw(1)
+       def keyOrMouseEvent(self, evt, val):
+               if (evt == Draw.ESCKEY and not val):
+                       Draw.Exit()
+       def buttonEvent(self, evt):
+               if (evt == 1):
+                       self.unfold()
+               if (evt == 5):
+                       try:
+                               Registry.GetKey('unfolder')['net'].setAvoidsOverlaps(self.overlaps.val)
+                       except:
+                               pass
+               if (evt == 2):
+                       print "Trying to set IPO curve"
+                       try:
+                               s = Blender.Object.GetSelected()
+                               if(s!=None):
+                                       Registry.GetKey('unfolder')['net'].setIPOSource( s[0] )
+                                       print "Set IPO curve"
+                               else:
+                                       print "Please select an object to use the IPO of"
+                       except:
+                               print "Problem setting IPO source"
+                       Draw.Redraw(1)
+               if (evt == 6):
+                       Draw.Exit()
+               if (evt == 7):
+                       try:
+                               if (Registry.GetKey('unfolder')['net']!=None):
+                                       Registry.GetKey('unfolder')['net'].animates = self.ani.val
+                                       if(self.ani):
+                                               Registry.GetKey('unfolder')['net'].sortOutIPOSource()
+                                               self.installScriptLink()
+                       except:
+                               print sys.exc_info()[1]
+                               traceback.print_exc(file=sys.stdout)
+                       Draw.Redraw(1)
+               if (evt == 19):
+                       pass
+               if (evt == 87):
+                       try:
+                               if (Registry.GetKey('unfolder')['net']!=None):
+                                       Registry.GetKey('unfolder')['net'].assignUVs()
+                                       self.say("Assigned UVs")
+                       except:
+                               print sys.exc_info()[1]
+                               traceback.print_exc(file=sys.stdout)
+                       Draw.Redraw(1)
+               if(evt==91):
+                       if( testOverlap() == True):
+                               self.nOverlaps = 1
+                       else:
+                               self.nOverlaps = 0
+                       Draw.Redraw(1)
+               if(evt==714):
+                       Net.unfoldAll(self)
+                       Draw.Redraw(1)
+               if(evt==713):
+                       self.iterator = self.iterators[self.shape.val]
+                       Draw.Redraw(1)
+               if(evt==92):
+                       if( testContains() == True):
+                               self.nOverlaps = 1
+                       else:
+                               self.nOverlaps = 0
+                       Draw.Redraw(1)
+               if(evt==104):
+                       try:
+                               filename = "net.svg"
+                               s = Blender.Object.GetSelected()
+                               if(s!=None and len(s)>0):
+                                       filename = s[0].getName()+".svg"
+                               else:
+                                       if (Registry.GetKey('unfolder')['net']!=None):
+                                               filename = Registry.GetKey('unfolder')['net'].des.name
+                                               if(filename==None):
+                                                       filename="net.svg"
+                                               else:
+                                                       filename=filename+".svg"
+                               Window.FileSelector(SVGExporter.fileSelected, "Select filename", filename)
+                       except:
+                               print "Problem exporting SVG"
+                               traceback.print_exc(file=sys.stdout)
+               if(evt==107):
+                       try:
+                               Window.FileSelector(NetHandler.fileSelected, "Select file")
+                       except:
+                               print "Problem importing SVG"
+                               traceback.print_exc(file=sys.stdout)
+       def say(self, m):
+               self.message = m
+               Draw.Redraw(1)
+               Window.Redraw(Window.Types.SCRIPT)
+       def draw(self):
+               cw = 64
+               ch = 16
+               l = FlowLayout(32, cw, ch, 350, 64)
+               l.y = 70
+               self.search = Draw.Toggle("search",     19,   l.nx(), l.ny(), l.cw, l.ch, self.search.val, "Search for non-overlapping mesh (potentially indefinitely)")
+               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")
+               self.ani = Draw.Toggle("ani",       7,   l.nx(), l.ny(), l.cw, l.ch, self.ani.val, "Animate net")
+               Draw.Button("uv",               87,   l.nx(), l.ny(), l.cw, l.ch, "Assign net as UV to source mesh (overwriting existing UV)")
+               Draw.Button("Unfold",           1, l.nx(), l.ny(), l.cw, l.ch, "Unfold selected mesh to net")
+               Draw.Button("save",             104,   l.nx(), l.ny(), l.cw, l.ch,  "Save net as SVG")
+               Draw.Button("load",             107,   l.nx(), l.ny(), l.cw, l.ch,  "Load net from SVG")
+               # unfolding enthusiasts - try uncommenting this
+               self.ancestors = Draw.Number("depth", 654,        l.nx(), l.ny(), cw, ch, self.ancestors.val, 0, 9999,  "depth of branching 0=diffuse")
+               #self.noise = Draw.Number("noise", 631,        l.nx(), l.ny(), cw, ch, self.noise.val, 0.0, 1.0,  "noisyness of branching")
+               #Draw.Button("UnfoldAll",           714, l.nx(), l.ny(), l.cw, l.ch, "Unfold all meshes and save their nets")
+               options = "order %t|random %x0|brightest %x1|curvature %x2|winding %x3| 1010 %x4|largest %x5"
+               self.shape = Draw.Menu(options, 713,  l.nx(), l.ny(), cw, ch, self.shape.val, "shape of net")
+               Draw.Button("exit",         6,   l.nx(), l.ny(), l.cw, l.ch, "exit")
+               BGL.glClearColor(0.5, 0.5, 0.5, 1)
+               BGL.glColor3f(0.3,0.3,0.3)
+               l.newLine()
+               BGL.glRasterPos2i(32, 100)
+               Draw.Text(self.message)
+
+class FlowLayout:
+       def __init__(self, margin, cw, ch, w, h):
+               self.x = margin-cw-4
+               self.y = margin
+               self.cw = cw
+               self.ch = ch
+               self.width = w
+               self.height = h
+               self.margin = margin
+       def nx(self):
+               self.x+=(self.cw+4)
+               if(self.x>self.width):
+                       self.x = self.margin
+                       self.y-=self.ch+4
+               return self.x
+       def ny(self):
+               return self.y
+       def newLine(self):
+               self.y-=self.ch+self.margin
+               self.x = self.margin
+
+try:
+       sys.setrecursionlimit(10000)
+       gui = GUI()
+       gui.makeStandardGUI()
+       #gui.makePopupGUI()
+except:
+       pass