+ newfaces.append(nf)
+
+ else:
+ nf = NMesh.Face()
+
+ nf.v.append(vl[0])
+ nf.v.append(vl[1])
+ nf.v.append(vl[2])
+ nf.v.append(vl[3])
+ newfaces.append(nf)
+
+ nf = NMesh.Face()
+ nf.v.append(vl[3])
+ nf.v.append(vl[4])
+ nf.v.append(vl[0])
+ newfaces.append(nf)
+
+ return newfaces
+
+ makeFaces = staticmethod(makeFaces)
+
+ def splitOn(Q, P, return_positive_faces=True, return_negative_faces=True):
+ """Split P using the plane of Q.
+ Logic taken from the knife.py python script
+ """
+
+ # Check if P and Q are parallel
+ 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:
+ 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
+
+ nP = len(P.v)
+
+ # Calculate point-plane Distance between vertices of P and plane Q
+ d = []
+ for i in range(0, nP):
+ d.append(HSR.Distance(P.v[i], Q))
+
+ newVertList = []
+
+ posVertList = []
+ negVertList = []
+ for i in range(nP):
+ d0 = d[i-1]
+ V0 = P.v[i-1]
+
+ d1 = d[i]
+ V1 = P.v[i]
+
+ #print "d0:", d0, "d1:", d1
+
+ # if the vertex lies in the cutplane
+ if abs(d1) < EPS:
+ #print "d1 On cutplane"
+ posVertList.append(V1)
+ negVertList.append(V1)
+ else:
+ # if the previous vertex lies in cutplane
+ if abs(d0) < EPS:
+ #print "d0 on Cutplane"
+ if d1 > 0:
+ #print "d1 on positive Halfspace"
+ posVertList.append(V1)
+ else:
+ #print "d1 on negative Halfspace"
+ negVertList.append(V1)
+ else:
+ # if they are on the same side of the plane
+ if d1*d0 > 0:
+ #print "On the same half-space"
+ if d1 > 0:
+ #print "d1 on positive Halfspace"
+ posVertList.append(V1)
+ else:
+ #print "d1 on negative Halfspace"
+ negVertList.append(V1)
+
+ # the vertices are not on the same side of the plane, so we have an intersection
+ else:
+ #print "Intersection"
+
+ 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)
+ if inters == None:
+ print "Split Break"
+ break
+
+ #print "Intersection", inters
+
+ nv = NMesh.Vert(inters[0], inters[1], inters[2])
+ newVertList.append(nv)
+
+ posVertList.append(nv)
+ negVertList.append(nv)
+
+ if d1 > 0:
+ posVertList.append(V1)
+ 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]'] ]
+
+ # a more portable way
+ 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??"
+ # posVertList = []
+ #if len(negVertList) < 3:
+ # print "Problem, we created a face with less that 3 vertices??"
+ # negVertList = []
+
+ if len(posVertList) < 3 or len(negVertList) < 3:
+ #print "RETURN NONE, SURE???"
+ return None
+
+ if not return_positive_faces:
+ posVertList = []
+ if not return_negative_faces:
+ negVertList = []
+
+ newfaces = HSR.addNewFaces(posVertList, negVertList)
+
+ return newfaces
+
+ splitOn = staticmethod(splitOn)
+
+ def addNewFaces(posVertList, negVertList):
+ # Create new faces resulting from the split
+ outfaces = []
+ 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]] )
+
+ for nf in newfaces:
+ if nf and len(nf)>2:
+ outfaces += HSR.makeFaces(nf)
+
+ return outfaces
+
+
+ addNewFaces = staticmethod(addNewFaces)
+
+
+# ---------------------------------------------------------------------
+#
+## Mesh Utility class
+#
+# ---------------------------------------------------------------------
+
+class MeshUtils:
+
+ def buildEdgeFaceUsersCache(me):
+ '''
+ Takes a mesh and returns a list aligned with the meshes edges.
+ Each item is a list of the faces that use the edge
+ would be the equiv for having ed.face_users as a property
+
+ Taken from .blender/scripts/bpymodules/BPyMesh.py,
+ thanks to ideasman_42.
+ '''
+
+ def sorted_edge_indicies(ed):
+ 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])
+ for f in me.faces:
+ 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]
+
+ if i1>i2:
+ i1,i2= i2,i1
+
+ face_edges_dict[i1,i2][1].append(f)
+
+ face_edges= [None] * len(me.edges)
+ for ed_index, ed_faces in face_edges_dict.itervalues():
+ face_edges[ed_index]= ed_faces
+
+ return face_edges
+
+ def isMeshEdge(adjacent_faces):
+ """Mesh edge rule.
+
+ A mesh edge is visible if _at_least_one_ of its adjacent faces is selected.
+ Note: if the edge has no adjacent faces we want to show it as well,
+ useful for "edge only" portion of objects.
+ """
+
+ if len(adjacent_faces) == 0:
+ return True
+
+ selected_faces = [f for f in adjacent_faces if f.sel]
+
+ if len(selected_faces) != 0:
+ return True
+ else:
+ return False
+
+ def isSilhouetteEdge(adjacent_faces):
+ """Silhuette selection rule.
+
+ An edge is a silhuette edge if it is shared by two faces with
+ different selection status or if it is a boundary edge of a selected
+ face.
+ """
+
+ if ((len(adjacent_faces) == 1 and adjacent_faces[0].sel == 1) or
+ (len(adjacent_faces) == 2 and
+ adjacent_faces[0].sel != adjacent_faces[1].sel)
+ ):
+ return True
+ else:
+ return False
+
+ buildEdgeFaceUsersCache = staticmethod(buildEdgeFaceUsersCache)
+ isMeshEdge = staticmethod(isMeshEdge)
+ isSilhouetteEdge = staticmethod(isSilhouetteEdge)
+
+
+# ---------------------------------------------------------------------
+#
+## Shading Utility class
+#
+# ---------------------------------------------------------------------
+
+class ShadingUtils:
+
+ shademap = None
+
+ 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]
+
+ return tmp_shademap
+
+ def toonShading(u):
+
+ shademap = ShadingUtils.shademap
+
+ if not shademap:
+ 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)
+
+ v = shademap[i+j]
+
+ if v < shademap[i+1]:
+ return v
+
+ return v
+
+ toonShadingMapSetup = staticmethod(toonShadingMapSetup)
+ toonShading = staticmethod(toonShading)
+
+
+# ---------------------------------------------------------------------
+#
+## Projections classes
+#
+# ---------------------------------------------------------------------
+
+class Projector:
+ """Calculate the projection of an object given the camera.
+
+ A projector is useful to so some per-object transformation to obtain the
+ projection of an object given the camera.
+
+ The main method is #doProjection# see the method description for the
+ parameter list.
+ """
+
+ def __init__(self, cameraObj, canvasRatio):
+ """Calculate the projection matrix.
+
+ The projection matrix depends, in this case, on the camera settings.
+ TAKE CARE: This projector expects vertices in World Coordinates!
+ """
+
+ camera = cameraObj.getData()
+
+ 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
+
+
+ if Blender.Get('version') < 243:
+ camPersp = 0
+ camOrtho = 1
+ else:
+ camPersp = 'persp'
+ camOrtho = 'ortho'
+
+ # What projection do we want?
+ if camera.type == camPersp:
+ mP = self._calcPerspectiveMatrix(fovy, aspect, near, far)
+ elif camera.type == camOrtho:
+ mP = self._calcOrthoMatrix(fovy, aspect, near, far, scale)
+
+
+ # View transformation
+ cam = Matrix(cameraObj.getInverseMatrix())
+ cam.transpose()
+
+ mP = mP * cam
+
+ self.projectionMatrix = mP
+
+ ##
+ # Public methods
+ #
+
+ def doProjection(self, v):
+ """Project the point on the view plane.
+
+ Given a vertex calculate the projection using the current projection
+ matrix.
+ """
+
+ # Note that we have to work on the vertex using homogeneous coordinates
+ # From blender 2.42+ we don't need to resize the vector to be 4d
+ # when applying a 4x4 matrix, but we do that anyway since we need the
+ # 4th coordinate later
+ p = self.projectionMatrix * Vector(v).resize4D()
+
+ # Perspective division
+ if p[3] != 0:
+ 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
+ p.resize3D()
+
+ return p
+
+
+ ##
+ # Private methods
+ #
+
+ def _calcPerspectiveMatrix(self, fovy, aspect, near, far):
+ """Return a perspective projection matrix.
+ """
+
+ 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))
+
+ m = Matrix(
+ [x, 0.0, a, 0.0],
+ [0.0, y, b, 0.0],
+ [0.0, 0.0, c, d],
+ [0.0, 0.0, -1.0, 0.0])
+
+ return m
+
+ def _calcOrthoMatrix(self, fovy, aspect , near, far, scale):
+ """Return an orthogonal projection matrix.
+ """
+
+ # The 11 in the formula was found emiprically
+ 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)
+
+ 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])
+
+ return m
+
+
+# ---------------------------------------------------------------------
+#
+## Progress Indicator
+#
+# ---------------------------------------------------------------------
+
+class Progress:
+ """A model for a progress indicator.
+
+ Do the progress calculation calculation and
+ the view independent stuff of a progress indicator.
+ """
+ def __init__(self, steps=0):
+ self.name = ""
+ self.steps = steps
+ self.completed = 0
+ self.progress = 0
+
+ def setSteps(self, steps):
+ """Set the number of steps of the activity wich we want to track.
+ """
+ self.steps = steps
+
+ def getSteps(self):
+ return self.steps
+
+ def setName(self, name):
+ """Set the name of the activity wich we want to track.
+ """
+ self.name = name
+
+ def getName(self):
+ return self.name
+
+ def getProgress(self):
+ return self.progress
+
+ def reset(self):
+ self.completed = 0
+ self.progress = 0
+
+ def update(self):
+ """Update the model, call this method when one step is completed.
+ """
+ if self.progress == 100:
+ return False
+
+ self.completed += 1
+ self.progress = ( float(self.completed) / float(self.steps) ) * 100
+ self.progress = int(self.progress)
+
+ return True
+
+
+class ProgressIndicator:
+ """An abstraction of a View for the Progress Model
+ """
+ def __init__(self):
+
+ # Use a refresh rate so we do not show the progress at
+ # every update, but every 'self.refresh_rate' times.
+ self.refresh_rate = 10
+ self.shows_counter = 0
+
+ self.quiet = False
+
+ self.progressModel = None
+
+ def setQuiet(self, value):
+ self.quiet = value
+
+ def setActivity(self, name, steps):
+ """Initialize the Model.
+
+ In a future version (with subactivities-progress support) this method
+ could only set the current activity.
+ """
+ self.progressModel = Progress()
+ self.progressModel.setName(name)
+ self.progressModel.setSteps(steps)
+
+ def getActivity(self):
+ return self.progressModel
+
+ def update(self):
+ """Update the model and show the actual progress.
+ """
+ assert(self.progressModel)
+
+ if self.progressModel.update():
+ if self.quiet:
+ return
+
+ self.show(self.progressModel.getProgress(),
+ self.progressModel.getName())
+
+ # We return always True here so we can call the update() method also
+ # from lambda funcs (putting the call in logical AND with other ops)
+ return True
+
+ def show(self, progress, name=""):
+ self.shows_counter = (self.shows_counter + 1) % self.refresh_rate
+ if self.shows_counter != 0:
+ return
+
+ if progress == 100:
+ self.shows_counter = -1
+
+
+class ConsoleProgressIndicator(ProgressIndicator):
+ """Show a progress bar on stderr, a la wget.
+ """
+ def __init__(self):
+ ProgressIndicator.__init__(self)
+
+ self.swirl_chars = ["-", "\\", "|", "/"]
+ self.swirl_count = -1
+
+ def show(self, progress, name):
+ ProgressIndicator.show(self, progress, name)
+
+ bar_length = 70
+ 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)
+ 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")
+ if progress == 100:
+ sys.stderr.write("\n")
+
+
+class GraphicalProgressIndicator(ProgressIndicator):
+ """Interface to the Blender.Window.DrawProgressBar() method.
+ """
+ def __init__(self):
+ ProgressIndicator.__init__(self)
+
+ #self.swirl_chars = ["-", "\\", "|", "/"]
+ # We have to use letters with the same width, for now!
+ # Blender progress bar considers the font widths when
+ # calculating the progress bar width.
+ self.swirl_chars = ["\\", "/"]
+ self.swirl_count = -1
+
+ def show(self, progress, name):
+ ProgressIndicator.show(self, progress)
+
+ 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)
+
+ if progress == 100:
+ Window.DrawProgressBar(1, progress_text)
+ Window.WaitCursor(0)
+
+
+
+# ---------------------------------------------------------------------
+#
+## 2D Object representation class
+#
+# ---------------------------------------------------------------------
+
+# TODO: a class to represent the needed properties of a 2D vector image
+# For now just using a [N]Mesh structure.
+
+
+# ---------------------------------------------------------------------
+#
+## Vector Drawing Classes
+#
+# ---------------------------------------------------------------------
+
+## A generic Writer
+
+class VectorWriter:
+ """
+ A class for printing output in a vectorial format.
+
+ Given a 2D representation of the 3D scene the class is responsible to
+ write it is a vector format.
+
+ Every subclasses of VectorWriter must have at last the following public
+ methods:
+ - open(self)
+ - close(self)
+ - printCanvas(self, scene,
+ doPrintPolygons=True, doPrintEdges=False, showHiddenEdges=False):
+ """
+
+ def __init__(self, fileName):
+ """Set the output file name and other properties"""
+
+ try:
+ config.writer
+ except:
+ config.writer = dict()
+ config.writer['SETTING'] = True
+
+ self.outputFileName = fileName
+
+ context = Scene.GetCurrent().getRenderingContext()
+ self.canvasSize = ( context.imageSizeX(), context.imageSizeY() )
+
+ self.fps = context.fps
+
+ self.startFrame = 1
+ self.endFrame = 1
+ self.animation = False
+
+
+ ##
+ # Public Methods
+ #
+
+ def open(self, startFrame=1, endFrame=1):
+ if startFrame != endFrame:
+ self.startFrame = startFrame
+ self.endFrame = endFrame
+ self.animation = True
+
+ print "Outputting to: ", self.outputFileName
+
+ return
+
+ def close(self):
+ return
+
+ def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False,
+ showHiddenEdges=False):
+ """This is the interface for the needed printing routine.
+ """
+ return
+
+
+## SVG Writer
+
+class SVGVectorWriter(VectorWriter):
+ """A concrete class for writing SVG output.
+ """
+
+ def __init__(self, fileName):
+ """Simply call the parent Contructor.
+ """
+ VectorWriter.__init__(self, fileName)
+
+ self.file = None
+
+
+ ##
+ # Public Methods
+ #
+
+ def open(self, startFrame=1, endFrame=1):
+ """Do some initialization operations.
+ """
+ VectorWriter.open(self, startFrame, endFrame)
+
+ self.file = open(self.outputFileName, "w")
+
+ self._printHeader()
+
+ def close(self):
+ """Do some finalization operation.
+ """
+ self._printFooter()
+
+ if self.file:
+ self.file.close()
+
+ # 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.
+ """
+
+ Objects = scene.objects
+
+ context = scene.getRenderingContext()
+ framenumber = context.currentFrame()
+
+ if self.animation:
+ framestyle = "display:none"
+ else:
+ framestyle = "display:block"
+
+ # Assign an id to this group so we can set properties on it using DOM
+ self.file.write("<g id=\"frame%d\" style=\"%s\">\n" %
+ (framenumber, framestyle) )
+
+
+ for obj in Objects:
+
+ if(obj.getType() != 'Mesh'):
+ continue
+
+ self.file.write("<g id=\"%s\">\n" % obj.getName())
+
+ mesh = obj.getData(mesh=1)
+
+ if doPrintPolygons:
+ self._printPolygons(mesh)
+
+ if doPrintEdges:
+ self._printEdges(mesh, showHiddenEdges)
+
+ self.file.write("</g>\n")
+
+ self.file.write("</g>\n")
+
+
+ ##
+ # Private Methods
+ #
+
+ def _calcCanvasCoord(self, v):
+ """Convert vertex in scene coordinates to canvas coordinates.
+ """
+
+ pt = Vector([0, 0, 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[2] = v.co[2]
+
+ # For now we want (0,0) in the top-left corner of the canvas.
+ # Mirror and translate along y
+ pt[1] *= -1
+ pt[1] += self.canvasSize[1]
+
+ return pt
+
+ def _printHeader(self):
+ """Print SVG header."""
+
+ self.file.write("<?xml version=\"1.0\"?>\n")
+ self.file.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n")
+ self.file.write("\t\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n")
+ self.file.write("<svg version=\"1.0\"\n")
+ self.file.write("\txmlns=\"http://www.w3.org/2000/svg\"\n")
+ self.file.write("\twidth=\"%d\" height=\"%d\">\n\n" %
+ self.canvasSize)
+
+ if self.animation:
+ delay = 1000/self.fps
+
+ self.file.write("""\n<script type="text/javascript"><![CDATA[
+ globalStartFrame=%d;
+ globalEndFrame=%d;
+
+ timerID = setInterval("NextFrame()", %d);
+ globalFrameCounter=%d;
+ \n""" % (self.startFrame, self.endFrame, delay, self.startFrame) )
+
+ self.file.write("""\n
+ function NextFrame()
+ {
+ currentElement = document.getElementById('frame'+globalFrameCounter)
+ previousElement = document.getElementById('frame'+(globalFrameCounter-1))
+
+ if (!currentElement)
+ {
+ return;
+ }
+
+ if (globalFrameCounter > globalEndFrame)
+ {
+ clearInterval(timerID)
+ }
+ else
+ {
+ if(previousElement)
+ {
+ previousElement.style.display="none";
+ }
+ currentElement.style.display="block";
+ globalFrameCounter++;
+ }
+ }
+ \n]]></script>\n
+ \n""")
+
+ def _printFooter(self):
+ """Print the SVG footer."""
+
+ self.file.write("\n</svg>\n")
+
+ def _printPolygons(self, mesh):
+ """Print the selected (visible) polygons.
+ """
+
+ if len(mesh.faces) == 0:
+ return
+
+ self.file.write("<g>\n")
+
+ for face in mesh.faces:
+ if not face.sel:
+ continue
+
+ self.file.write("<path d=\"")
+
+ #p = self._calcCanvasCoord(face.verts[0])
+ p = self._calcCanvasCoord(face.v[0])
+ self.file.write("M %g,%g L " % (p[0], p[1]))
+
+ for v in face.v[1:]:
+ p = self._calcCanvasCoord(v)
+ self.file.write("%g,%g " % (p[0], p[1]))
+
+ # get rid of the last blank space, just cosmetics here.
+ self.file.seek(-1, 1)
+ self.file.write(" z\"\n")
+
+ # take as face color the first vertex color
+ if face.col:
+ fcol = face.col[0]
+ color = [fcol.r, fcol.g, fcol.b, fcol.a]
+ else:
+ color = [255, 255, 255, 255]
+
+ # Convert the color to the #RRGGBB form
+ str_col = "#%02X%02X%02X" % (color[0], color[1], color[2])
+
+ # Handle transparent polygons
+ opacity_string = ""
+ if color[3] != 255:
+ opacity = float(color[3])/255.0
+ opacity_string = " fill-opacity: %g; stroke-opacity: %g; opacity: 1;" % (opacity, opacity)
+ #opacity_string = "opacity: %g;" % (opacity)
+
+ self.file.write("\tstyle=\"fill:" + str_col + ";")
+ self.file.write(opacity_string)
+
+ # use the stroke property to alleviate the "adjacent edges" problem,
+ # we simulate polygon expansion using borders,
+ # see http://www.antigrain.com/svg/index.html for more info
+ stroke_width = 1.0
+
+ # EXPANSION TRICK is not that useful where there is transparency
+ if config.polygons['EXPANSION_TRICK'] and color[3] == 255:
+ # str_col = "#000000" # For debug
+ self.file.write(" stroke:%s;\n" % str_col)
+ self.file.write(" stroke-width:" + str(stroke_width) + ";\n")
+ self.file.write(" stroke-linecap:round;stroke-linejoin:round")
+
+ self.file.write("\"/>\n")
+
+ self.file.write("</g>\n")
+
+ def _printEdges(self, mesh, showHiddenEdges=False):
+ """Print the wireframe using mesh edges.
+ """
+
+ stroke_width = config.edges['WIDTH']
+ stroke_col = config.edges['COLOR']
+
+ self.file.write("<g>\n")
+
+ for e in mesh.edges:
+
+ hidden_stroke_style = ""
+
+ if e.sel == 0:
+ if showHiddenEdges == False:
+ continue
+ else:
+ hidden_stroke_style = ";\n stroke-dasharray:3, 3"
+
+ p1 = self._calcCanvasCoord(e.v1)
+ p2 = self._calcCanvasCoord(e.v2)
+
+ self.file.write("<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"\n"
+ % ( p1[0], p1[1], p2[0], p2[1] ) )
+ self.file.write(" style=\"stroke:rgb("+str(stroke_col[0])+","+str(stroke_col[1])+","+str(stroke_col[2])+");")
+ self.file.write(" stroke-width:"+str(stroke_width)+";\n")
+ self.file.write(" stroke-linecap:round;stroke-linejoin:round")
+ self.file.write(hidden_stroke_style)
+ self.file.write("\"/>\n")
+
+ self.file.write("</g>\n")
+
+
+## SWF Writer
+
+try:
+ from ming import *
+ SWFSupported = True
+except:
+ SWFSupported = False
+
+class SWFVectorWriter(VectorWriter):
+ """A concrete class for writing SWF output.
+ """
+
+ def __init__(self, fileName):
+ """Simply call the parent Contructor.
+ """
+ VectorWriter.__init__(self, fileName)
+
+ self.movie = None
+ self.sprite = None
+
+
+ ##
+ # Public Methods
+ #
+
+ def open(self, startFrame=1, endFrame=1):
+ """Do some initialization operations.
+ """
+ VectorWriter.open(self, startFrame, endFrame)
+ self.movie = SWFMovie()
+ self.movie.setDimension(self.canvasSize[0], self.canvasSize[1])
+ if self.animation:
+ self.movie.setRate(self.fps)
+ numframes = endFrame - startFrame + 1
+ self.movie.setFrames(numframes)
+
+ def close(self):
+ """Do some finalization operation.
+ """
+ self.movie.save(self.outputFileName)
+
+ # remember to call the close method of the parent
+ VectorWriter.close(self)
+
+ def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False,
+ showHiddenEdges=False):
+ """Convert the scene representation to SVG.
+ """
+ context = scene.getRenderingContext()
+ framenumber = context.currentFrame()
+
+ Objects = scene.objects
+
+ if self.sprite:
+ self.movie.remove(self.sprite)
+
+ sprite = SWFSprite()
+
+ for obj in Objects:
+
+ if(obj.getType() != 'Mesh'):
+ continue
+
+ mesh = obj.getData(mesh=1)
+
+ if doPrintPolygons:
+ self._printPolygons(mesh, sprite)
+
+ if doPrintEdges:
+ self._printEdges(mesh, sprite, showHiddenEdges)
+
+ sprite.nextFrame()
+ i = self.movie.add(sprite)
+ # Remove the instance the next time
+ self.sprite = i
+ if self.animation:
+ self.movie.nextFrame()
+
+
+ ##
+ # Private Methods
+ #
+
+ def _calcCanvasCoord(self, v):
+ """Convert vertex in scene coordinates to canvas coordinates.
+ """
+
+ pt = Vector([0, 0, 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[2] = v.co[2]
+
+ # For now we want (0,0) in the top-left corner of the canvas.
+ # Mirror and translate along y
+ pt[1] *= -1
+ pt[1] += self.canvasSize[1]
+
+ return pt
+
+ def _printPolygons(self, mesh, sprite):
+ """Print the selected (visible) polygons.
+ """
+
+ if len(mesh.faces) == 0:
+ return
+
+ for face in mesh.faces:
+ if not face.sel:
+ continue
+
+ if face.col:
+ fcol = face.col[0]
+ color = [fcol.r, fcol.g, fcol.b, fcol.a]
+ else:
+ color = [255, 255, 255, 255]
+
+ s = SWFShape()
+ f = s.addFill(color[0], color[1], color[2], color[3])
+ s.setRightFill(f)
+
+ # The starting point of the shape
+ p0 = self._calcCanvasCoord(face.verts[0])
+ s.movePenTo(p0[0], p0[1])
+
+ for v in face.verts[1:]:
+ p = self._calcCanvasCoord(v)
+ s.drawLineTo(p[0], p[1])
+
+ # Closing the shape
+ s.drawLineTo(p0[0], p0[1])
+
+ s.end()
+ sprite.add(s)
+
+
+ def _printEdges(self, mesh, sprite, showHiddenEdges=False):
+ """Print the wireframe using mesh edges.
+ """