Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2007-03-18 15:08:51 +0300
committerCampbell Barton <ideasman42@gmail.com>2007-03-18 15:08:51 +0300
commit15de493426279be4f7d1362b2553667d610a9445 (patch)
tree0fa8887f06823599ccc48dac9466e6d9a95e852a /release/scripts/mesh_unfolder.py
parent5a9082a193d4dd741454e574bbc844e99cbb2d65 (diff)
excellent mesh unolder script by Matthew Chadwick
http://celeriac.net/unfolder/
Diffstat (limited to 'release/scripts/mesh_unfolder.py')
-rw-r--r--release/scripts/mesh_unfolder.py1601
1 files changed, 1601 insertions, 0 deletions
diff --git a/release/scripts/mesh_unfolder.py b/release/scripts/mesh_unfolder.py
new file mode 100644
index 00000000000..f725da31837
--- /dev/null
+++ b/release/scripts/mesh_unfolder.py
@@ -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