X-Git-Url: https://git.ao2.it/vrm.git/blobdiff_plain/de221ecd94607f731bb5d39c4d71447e3b545098..efa292dcd4cfb88bb0a1d45147c408e500a86d73:/vrm.py diff --git a/vrm.py b/vrm.py index 15fba09..86c8fba 100755 --- a/vrm.py +++ b/vrm.py @@ -8,14 +8,14 @@ Tooltip: 'Vector Rendering Method script' __author__ = "Antonio Ospite" __url__ = ["http://vrm.ao2.it"] -__version__ = "0.3.beta" +__version__ = "0.3" __bpydoc__ = """\ Render the scene and save the result in vector format. """ # --------------------------------------------------------------------- -# Copyright (c) 2006, 2007, 2008, 2009 Antonio Ospite +# Copyright (c) 2006, 2007, 2008, 2009, 2012 Antonio Ospite # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -66,7 +66,8 @@ import Blender from Blender import Scene, Object, Mesh, NMesh, Material, Lamp, Camera, Window from Blender.Mathutils import * from math import * -import sys, time +import sys +import time try: set() @@ -76,7 +77,7 @@ except NameError: def uniq(alist): tmpdict = dict() - return [tmpdict.setdefault(e,e) for e in alist if e not in tmpdict] + return [tmpdict.setdefault(e, e) for e in alist if e not in tmpdict] # in python > 2.4 we ca use the following #return [ u for u in alist if u not in locals()['_[1]'] ] @@ -93,8 +94,8 @@ progress = None class config: polygons = dict() polygons['SHOW'] = True - polygons['SHADING'] = 'FLAT' # FLAT or TOON - polygons['HSR'] = 'PAINTER' # PAINTER or NEWELL + polygons['SHADING'] = 'FLAT' # FLAT or TOON + polygons['HSR'] = 'PAINTER' # PAINTER or NEWELL # Hidden to the user for now polygons['EXPANSION_TRICK'] = True @@ -103,7 +104,7 @@ class config: edges = dict() edges['SHOW'] = False edges['SHOW_HIDDEN'] = False - edges['STYLE'] = 'MESH' # MESH or SILHOUETTE + edges['STYLE'] = 'MESH' # MESH or SILHOUETTE edges['WIDTH'] = 2 edges['COLOR'] = [0, 0, 0] @@ -115,14 +116,14 @@ class config: def saveToRegistry(): registry = {} - for k,v in config.__dict__.iteritems(): + for k, v in config.__dict__.iteritems(): # config class store settings in dictionaries if v.__class__ == dict().__class__: - regkey_prefix = k.upper()+"_" + regkey_prefix = k.upper() + "_" - for opt_k,opt_v in v.iteritems(): + for opt_k, opt_v in v.iteritems(): regkey = regkey_prefix + opt_k registry[regkey] = opt_v @@ -134,15 +135,15 @@ class config: def loadFromRegistry(): registry = Blender.Registry.GetKey('VRM', True) if not registry: - return + return - for k,v in registry.iteritems(): + for k, v in registry.iteritems(): k_tmp = k.split('_') conf_attr = k_tmp[0].lower() - conf_key = str.join("_",k_tmp[1:]) + conf_key = str.join("_", k_tmp[1:]) conf_val = v - if config.__dict__.has_key(conf_attr): + if conf_attr in config.__dict__: config.__dict__[conf_attr][conf_key] = conf_val loadFromRegistry = staticmethod(loadFromRegistry) @@ -151,6 +152,7 @@ class config: # Utility functions print_debug = False + def dumpfaces(flist, filename): """Dump a single face to a file. """ @@ -170,17 +172,20 @@ def dumpfaces(flist, filename): writerobj.close() + def debug(msg): if print_debug: sys.stderr.write(msg) + def EQ(v1, v2): - return (abs(v1[0]-v2[0]) < EPS and - abs(v1[1]-v2[1]) < EPS ) + return (abs(v1[0] - v2[0]) < EPS and + abs(v1[1] - v2[1]) < EPS) by_furthest_z = (lambda f1, f2: - cmp(max([v.co[2] for v in f1]), max([v.co[2] for v in f2])+EPS) + cmp(max([v.co[2] for v in f1]), max([v.co[2] for v in f2]) + EPS) ) + def sign(x): if x < -EPS: @@ -202,6 +207,7 @@ def sign(x): EPS = 10e-5 INF = 10e5 + class HSR: """A utility class for HSR processing. """ @@ -212,9 +218,9 @@ class HSR: From: http://mathworld.wolfram.com/Coplanar.html Geometric objects lying in a common plane are said to be coplanar. - Three noncollinear points determine a plane and so are trivially coplanar. - Four points are coplanar iff the volume of the tetrahedron defined by them is - 0, + Three noncollinear points determine a plane and so are trivially + coplanar. Four points are coplanar iff the volume of the tetrahedron + defined by them is 0, | x_1 y_1 z_1 1 | | x_2 y_2 z_2 1 | @@ -222,8 +228,8 @@ class HSR: | x_4 y_4 z_4 1 | == 0 Coplanarity is equivalent to the statement that the pair of lines - determined by the four points are not skew, and can be equivalently stated - in vector form as (x_3-x_1).[(x_2-x_1)x(x_4-x_3)]==0. + determined by the four points are not skew, and can be equivalently + stated in vector form as (x_3-x_1).[(x_2-x_1)x(x_4-x_3)]==0. An arbitrary number of n points x_1, ..., x_n can be tested for coplanarity by finding the point-plane distances of the points @@ -243,13 +249,13 @@ class HSR: elif n == 3: # three points must be complanar return False - else: # n == 4 + else: # n == 4 x1 = Vector(face[0].co) x2 = Vector(face[1].co) x3 = Vector(face[2].co) x4 = Vector(face[3].co) - v = (x3-x1) * CrossVecs((x2-x1), (x4-x3)) + v = (x3 - x1) * CrossVecs((x2 - x1), (x4 - x3)) if v != 0: return True @@ -275,25 +281,24 @@ class HSR: #z3 = s2[0].co[2] #z4 = s2[1].co[2] - # calculate delta values (vector components) - dx1 = x2 - x1; - dx2 = x4 - x3; - dy1 = y2 - y1; - dy2 = y4 - y3; + dx1 = x2 - x1 + dx2 = x4 - x3 + dy1 = y2 - y1 + dy2 = y4 - y3 - #dz1 = z2 - z1; - #dz2 = z4 - z3; + #dz1 = z2 - z1 + #dz2 = z4 - z3 - C = dy2 * dx1 - dx2 * dy1 # /* cross product */ - if C == 0: #/* parallel */ + C = dy2 * dx1 - dx2 * dy1 # cross product + if C == 0: # parallel return None - dx3 = x1 - x3 # /* combined origin offset vector */ + dx3 = x1 - x3 # combined origin offset vector dy3 = y1 - y3 - a1 = (dy3 * dx2 - dx3 * dy2) / C; - a2 = (dy3 * dx1 - dx3 * dy1) / C; + a1 = (dy3 * dx2 - dx3 * dy2) / C + a2 = (dy3 * dx1 - dx3 * dy1) / C # check for degeneracies #print_debug("\n") @@ -304,11 +309,11 @@ class HSR: # Intersection on boundaries, we consider the point external? return None - elif (a1>0.0 and a1<1.0 and a2>0.0 and a2<1.0): # /* lines cross */ - x = x1 + a1*dx1 - y = y1 + a1*dy1 + elif (a1 > 0.0 and a1 < 1.0 and a2 > 0.0 and a2 < 1.0): # lines cross + x = x1 + a1 * dx1 + y = y1 + a1 * dy1 - #z = z1 + a1*dz1 + #z = z1 + a1 * dz1 z = 0 return (NMesh.Vert(x, y, z), a1, a2) @@ -327,7 +332,7 @@ class HSR: for i in range(len(self.v)): s1 = (point_at_infinity, v) - s2 = (self.v[i-1], self.v[i]) + s2 = (self.v[i - 1], self.v[i]) if EQ(v.co, s2[0].co) or EQ(v.co, s2[1].co): coincidence = True @@ -336,7 +341,7 @@ class HSR: winding_number += 1 # Check even or odd - if winding_number % 2 == 0 : + if (winding_number % 2) == 0: return False else: if coincidence: @@ -345,10 +350,9 @@ class HSR: isVertInside = staticmethod(isVertInside) - def det(a, b, c): return ((b[0] - a[0]) * (c[1] - a[1]) - - (b[1] - a[1]) * (c[0] - a[0]) ) + (b[1] - a[1]) * (c[0] - a[0])) det = staticmethod(det) @@ -360,13 +364,13 @@ class HSR: det = HSR.det for i in range(len(P.v)): - p0 = P.v[i-1] + p0 = P.v[i - 1] p1 = P.v[i] - if (det(q.co, point_at_infinity.co, p0.co)<0) != (det(q.co, point_at_infinity.co, p1.co)<0): - if det(p0.co, p1.co, q.co) == 0 : + if (det(q.co, point_at_infinity.co, p0.co) < 0) != (det(q.co, point_at_infinity.co, p1.co) < 0): + if det(p0.co, p1.co, q.co) == 0: #print "On Boundary" return False - elif (det(p0.co, p1.co, q.co)<0) != (det(p0.co, p1.co, point_at_infinity.co)<0): + elif (det(p0.co, p1.co, q.co) < 0) != (det(p0.co, p1.co, point_at_infinity.co) < 0): is_in = not is_in return is_in @@ -386,7 +390,6 @@ class HSR: for i in range(len(f1.v)): - # If a point of f1 in inside f2, there is an overlap! v1 = f1.v[i] #if HSR.isVertInside(f2, v1): @@ -396,10 +399,10 @@ class HSR: # If not the polygon can be ovelap as well, so we check for # intersection between an edge of f1 and all the edges of f2 - v0 = f1.v[i-1] + v0 = f1.v[i - 1] for j in range(len(f2.v)): - v2 = f2.v[j-1] + v2 = f2.v[j - 1] v3 = f2.v[j] e1 = v0, v1 @@ -433,7 +436,7 @@ class HSR: """ by_furthest_z = (lambda f1, f2: - cmp(max([v.co[2] for v in f1]), max([v.co[2] for v in f2])+EPS) + cmp(max([v.co[2] for v in f1]), max([v.co[2] for v in f2]) + EPS) ) # Choose if split P on Q plane or vice-versa @@ -497,7 +500,6 @@ class HSR: # #newfaces = splitOn(plane, f) - if newfaces == None: print "Big FAT problem, we weren't able to split POLYGONS!" raise AssertionError @@ -537,8 +539,8 @@ class HSR: """Check if point p is in segment v1v2. """ - l1 = (v1-p).length - l2 = (v2-p).length + l1 = (v1 - p).length + l2 = (v2 - p).length # Should we consider extreme points as internal ? # The test: @@ -546,10 +548,10 @@ class HSR: if l1 < EPS or l2 < EPS: return extremes_internal - l = (v1-v2).length + l = (v1 - v2).length # if the sum of l1 and l2 is circa l, then the point is on segment, - if abs(l - (l1+l2)) < EPS: + if abs(l - (l1 + l2)) < EPS: return True else: return False @@ -622,16 +624,15 @@ class HSR: """ # Check if P and Q are parallel - u = CrossVecs(Vector(Q.no),Vector(P.no)) + u = CrossVecs(Vector(Q.no), Vector(P.no)) ax = abs(u[0]) ay = abs(u[1]) az = abs(u[2]) - if (ax+ay+az) < EPS: + if (ax + ay + az) < EPS: print "PARALLEL planes!!" return - # The final aim is to find the intersection line between P # and the plane of Q, and split P along this line @@ -647,8 +648,8 @@ class HSR: posVertList = [] negVertList = [] for i in range(nP): - d0 = d[i-1] - V0 = P.v[i-1] + d0 = d[i - 1] + V0 = P.v[i - 1] d1 = d[i] V1 = P.v[i] @@ -672,7 +673,7 @@ class HSR: negVertList.append(V1) else: # if they are on the same side of the plane - if d1*d0 > 0: + if (d1 * d0) > 0: #print "On the same half-space" if d1 > 0: #print "d1 on positive Halfspace" @@ -688,7 +689,7 @@ class HSR: e = Vector(V0), Vector(V1) tri = Vector(Q[0]), Vector(Q[1]), Vector(Q[2]) - inters = Intersect(tri[0], tri[1], tri[2], e[1]-e[0], e[0], 0) + inters = Intersect(tri[0], tri[1], tri[2], e[1] - e[0], e[0], 0) if inters == None: print "Split Break" break @@ -706,7 +707,6 @@ class HSR: else: negVertList.append(V1) - # uniq for python > 2.4 #posVertList = [ u for u in posVertList if u not in locals()['_[1]'] ] #negVertList = [ u for u in negVertList if u not in locals()['_[1]'] ] @@ -715,7 +715,6 @@ class HSR: posVertList = uniq(posVertList) negVertList = uniq(negVertList) - # If vertex are all on the same half-space, return #if len(posVertList) < 3: # print "Problem, we created a face with less that 3 vertices??" @@ -745,16 +744,15 @@ class HSR: if len(posVertList) or len(negVertList): #newfaces = [posVertList] + [negVertList] - newfaces = ( [[ NMesh.Vert(v[0], v[1], v[2]) for v in posVertList]] + - [[ NMesh.Vert(v[0], v[1], v[2]) for v in negVertList]] ) + newfaces = ([[NMesh.Vert(v[0], v[1], v[2]) for v in posVertList]] + + [[NMesh.Vert(v[0], v[1], v[2]) for v in negVertList]]) for nf in newfaces: - if nf and len(nf)>2: + if nf and len(nf) > 2: outfaces += HSR.makeFaces(nf) return outfaces - addNewFaces = staticmethod(addNewFaces) @@ -777,28 +775,27 @@ class MeshUtils: ''' def sorted_edge_indicies(ed): - i1= ed.v1.index - i2= ed.v2.index - if i1>i2: - i1,i2= i2,i1 + i1 = ed.v1.index + i2 = ed.v2.index + if i1 > i2: + i1, i2 = i2, i1 return i1, i2 - - face_edges_dict= dict([(sorted_edge_indicies(ed), (ed.index, [])) for ed in me.edges]) + face_edges_dict = dict([(sorted_edge_indicies(ed), (ed.index, [])) for ed in me.edges]) for f in me.faces: - fvi= [v.index for v in f.v]# face vert idx's + fvi = [v.index for v in f.v] # face vert idx's for i in xrange(len(f)): - i1= fvi[i] - i2= fvi[i-1] + i1 = fvi[i] + i2 = fvi[i - 1] - if i1>i2: - i1,i2= i2,i1 + if i1 > i2: + i1, i2 = i2, i1 - face_edges_dict[i1,i2][1].append(f) + face_edges_dict[i1, i2][1].append(f) - face_edges= [None] * len(me.edges) + face_edges = [None] * len(me.edges) for ed_index, ed_faces in face_edges_dict.itervalues(): - face_edges[ed_index]= ed_faces + face_edges[ed_index] = ed_faces return face_edges @@ -854,8 +851,8 @@ class ShadingUtils: def toonShadingMapSetup(): levels = config.polygons['TOON_LEVELS'] - texels = 2*levels - 1 - tmp_shademap = [0.0] + [(i)/float(texels-1) for i in xrange(1, texels-1) ] + [1.0] + texels = 2 * levels - 1 + tmp_shademap = [0.0] + [(i) / float(texels - 1) for i in xrange(1, texels - 1)] + [1.0] return tmp_shademap @@ -867,13 +864,13 @@ class ShadingUtils: shademap = ShadingUtils.toonShadingMapSetup() v = 1.0 - for i in xrange(0, len(shademap)-1): - pivot = (shademap[i]+shademap[i+1])/2.0 - j = int(u>pivot) + for i in xrange(0, len(shademap) - 1): + pivot = (shademap[i] + shademap[i + 1]) / 2.0 + j = int(u > pivot) - v = shademap[i+j] + v = shademap[i + j] - if v < shademap[i+1]: + if v < shademap[i + 1]: return v return v @@ -907,15 +904,14 @@ class Projector: camera = cameraObj.getData() - aspect = float(canvasRatio[0])/float(canvasRatio[1]) + aspect = float(canvasRatio[0]) / float(canvasRatio[1]) near = camera.clipStart far = camera.clipEnd scale = float(camera.scale) - fovy = atan(0.5/aspect/(camera.lens/32)) - fovy = fovy * 360.0/pi - + fovy = atan(0.5 / aspect / (camera.lens / 32)) + fovy = fovy * 360.0 / pi if Blender.Get('version') < 243: camPersp = 0 @@ -930,7 +926,6 @@ class Projector: elif camera.type == camOrtho: mP = self._calcOrthoMatrix(fovy, aspect, near, far, scale) - # View transformation cam = Matrix(cameraObj.getInverseMatrix()) cam.transpose() @@ -958,9 +953,9 @@ class Projector: # Perspective division if p[3] != 0: - p[0] = p[0]/p[3] - p[1] = p[1]/p[3] - p[2] = p[2]/p[3] + p[0] = p[0] / p[3] + p[1] = p[1] / p[3] + p[2] = p[2] / p[3] # restore the size p[3] = 1.0 @@ -968,7 +963,6 @@ class Projector: return p - ## # Private methods # @@ -979,14 +973,14 @@ class Projector: top = near * tan(fovy * pi / 360.0) bottom = -top - left = bottom*aspect - right= top*aspect - x = (2.0 * near) / (right-left) - y = (2.0 * near) / (top-bottom) - a = (right+left) / (right-left) - b = (top+bottom) / (top - bottom) - c = - ((far+near) / (far-near)) - d = - ((2*far*near)/(far-near)) + left = bottom * aspect + right = top * aspect + x = (2.0 * near) / (right - left) + y = (2.0 * near) / (top - bottom) + a = (right + left) / (right - left) + b = (top + bottom) / (top - bottom) + c = - ((far + near) / (far - near)) + d = - ((2 * far * near) / (far - near)) m = Matrix( [x, 0.0, a, 0.0], @@ -996,7 +990,7 @@ class Projector: return m - def _calcOrthoMatrix(self, fovy, aspect , near, far, scale): + def _calcOrthoMatrix(self, fovy, aspect, near, far, scale): """Return an orthogonal projection matrix. """ @@ -1004,19 +998,19 @@ class Projector: top = near * tan(fovy * pi / 360.0) * (scale * 11) bottom = -top left = bottom * aspect - right= top * aspect - rl = right-left - tb = top-bottom - fn = near-far - tx = -((right+left)/rl) - ty = -((top+bottom)/tb) - tz = ((far+near)/fn) + right = top * aspect + rl = right - left + tb = top - bottom + fn = near - far + tx = -((right + left) / rl) + ty = -((top + bottom) / tb) + tz = ((far + near) / fn) m = Matrix( - [2.0/rl, 0.0, 0.0, tx], - [0.0, 2.0/tb, 0.0, ty], - [0.0, 0.0, 2.0/fn, tz], - [0.0, 0.0, 0.0, 1.0]) + [2.0 / rl, 0.0, 0.0, tx], + [0.0, 2.0 / tb, 0.0, ty], + [0.0, 0.0, 2.0 / fn, tz], + [0.0, 0.0, 0.0, 1.0]) return m @@ -1069,7 +1063,7 @@ class Progress: return False self.completed += 1 - self.progress = ( float(self.completed) / float(self.steps) ) * 100 + self.progress = (float(self.completed) / float(self.steps)) * 100 self.progress = int(self.progress) return True @@ -1143,15 +1137,15 @@ class ConsoleProgressIndicator(ProgressIndicator): ProgressIndicator.show(self, progress, name) bar_length = 70 - bar_progress = int( (progress/100.0) * bar_length ) + bar_progress = int((progress / 100.0) * bar_length) bar = ("=" * bar_progress).ljust(bar_length) - self.swirl_count = (self.swirl_count+1)%len(self.swirl_chars) + self.swirl_count = (self.swirl_count + 1) % len(self.swirl_chars) swirl_char = self.swirl_chars[self.swirl_count] progress_bar = "%s |%s| %c %3d%%" % (name, bar, swirl_char, progress) - sys.stderr.write(progress_bar+"\r") + sys.stderr.write(progress_bar + "\r") if progress == 100: sys.stderr.write("\n") @@ -1172,21 +1166,19 @@ class GraphicalProgressIndicator(ProgressIndicator): def show(self, progress, name): ProgressIndicator.show(self, progress) - self.swirl_count = (self.swirl_count+1)%len(self.swirl_chars) + self.swirl_count = (self.swirl_count + 1) % len(self.swirl_chars) swirl_char = self.swirl_chars[self.swirl_count] progress_text = "%s - %c %3d%%" % (name, swirl_char, progress) # Finally draw the Progress Bar - Window.WaitCursor(1) # Maybe we can move that call in the constructor? - Window.DrawProgressBar(progress/100.0, progress_text) + Window.WaitCursor(1) # Maybe we can move that call in the constructor? + Window.DrawProgressBar(progress / 100.0, progress_text) if progress == 100: Window.DrawProgressBar(1, progress_text) Window.WaitCursor(0) - - # --------------------------------------------------------------------- # ## 2D Object representation class @@ -1232,7 +1224,7 @@ class VectorWriter: self.outputFileName = fileName context = Scene.GetCurrent().getRenderingContext() - self.canvasSize = ( context.imageSizeX(), context.imageSizeY() ) + self.canvasSize = (context.imageSizeX(), context.imageSizeY()) self.fps = context.fps @@ -1240,7 +1232,6 @@ class VectorWriter: self.endFrame = 1 self.animation = False - ## # Public Methods # @@ -1278,7 +1269,6 @@ class SVGVectorWriter(VectorWriter): self.file = None - ## # Public Methods # @@ -1303,7 +1293,6 @@ class SVGVectorWriter(VectorWriter): # remember to call the close method of the parent as last VectorWriter.close(self) - def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False, showHiddenEdges=False): """Convert the scene representation to SVG. @@ -1321,12 +1310,11 @@ class SVGVectorWriter(VectorWriter): # Assign an id to this group so we can set properties on it using DOM self.file.write("\n" % - (framenumber, framestyle) ) - + (framenumber, framestyle)) for obj in Objects: - if(obj.getType() != 'Mesh'): + if obj.getType() != 'Mesh': continue self.file.write("\n" % obj.getName()) @@ -1343,7 +1331,6 @@ class SVGVectorWriter(VectorWriter): self.file.write("\n") - ## # Private Methods # @@ -1354,12 +1341,12 @@ class SVGVectorWriter(VectorWriter): pt = Vector([0, 0, 0]) - mW = float(self.canvasSize[0])/2.0 - mH = float(self.canvasSize[1])/2.0 + mW = float(self.canvasSize[0]) / 2.0 + mH = float(self.canvasSize[1]) / 2.0 # rescale to canvas size - pt[0] = v.co[0]*mW + mW - pt[1] = v.co[1]*mH + mH + pt[0] = v.co[0] * mW + mW + pt[1] = v.co[1] * mH + mH pt[2] = v.co[2] # For now we want (0,0) in the top-left corner of the canvas. @@ -1381,7 +1368,7 @@ class SVGVectorWriter(VectorWriter): self.canvasSize) if self.animation: - delay = 1000/self.fps + delay = 1000 / self.fps self.file.write("""\n