6 Tooltip: 'Vector Rendering Method Export Script'
9 __author__ = "Antonio Ospite"
14 Render the scene and save the result in vector format.
17 # ---------------------------------------------------------------------
18 # Copyright (c) 2006 Antonio Ospite
20 # This program is free software; you can redistribute it and/or modify
21 # it under the terms of the GNU General Public License as published by
22 # the Free Software Foundation; either version 2 of the License, or
23 # (at your option) any later version.
25 # This program is distributed in the hope that it will be useful,
26 # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 # GNU General Public License for more details.
30 # You should have received a copy of the GNU General Public License
31 # along with this program; if not, write to the Free Software
32 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34 # ---------------------------------------------------------------------
37 # Thanks to Emilio Aguirre for S2flender from which I took inspirations :)
38 # Thanks to Nikola Radovanovic, the author of the original VRM script,
39 # the code you read here has been rewritten _almost_ entirely
40 # from scratch but Nikola gave me the idea, so I thank him publicly.
42 # ---------------------------------------------------------------------
44 # Things TODO for a next release:
45 # - Switch to the Mesh structure, should be considerably faster
46 # (partially done, but cannot sort faces, yet)
47 # - Use a better depth sorting algorithm
48 # - Review how selections are made (this script uses selection states of
49 # primitives to represent visibility infos)
50 # - Implement clipping of primitives and do handle object intersections.
51 # (for now only clipping for whole objects is supported).
52 # - Implement Edge Styles (silhouettes, contours, etc.)
53 # - Implement Edge coloring
54 # - Use multiple lighting sources in color calculation
55 # - Implement Shading Styles?
56 # - Use another representation for the 2D projection?
57 # Think to a way to merge adjacent polygons that have the same color.
58 # - Add other Vector Writers.
60 # ---------------------------------------------------------------------
64 # vrm-0.3.py - 2006-05-19
65 # * First release after code restucturing.
66 # Now the script offers a useful set of functionalities
67 # and it can render animations, too.
69 # ---------------------------------------------------------------------
72 from Blender import Scene, Object, Mesh, NMesh, Material, Lamp, Camera
73 from Blender.Mathutils import *
77 # Some global settings
80 SHOW_HIDDEN_EDGES = False
84 POLYGON_EXPANSION_TRICK = True
86 RENDER_ANIMATION = False
88 # Does not work in batch mode!!
89 #OPTIMIZE_FOR_SPACE = True
92 # ---------------------------------------------------------------------
94 ## Projections classes
96 # ---------------------------------------------------------------------
99 """Calculate the projection of an object given the camera.
101 A projector is useful to so some per-object transformation to obtain the
102 projection of an object given the camera.
104 The main method is #doProjection# see the method description for the
108 def __init__(self, cameraObj, canvasRatio):
109 """Calculate the projection matrix.
111 The projection matrix depends, in this case, on the camera settings.
112 TAKE CARE: This projector expects vertices in World Coordinates!
115 camera = cameraObj.getData()
117 aspect = float(canvasRatio[0])/float(canvasRatio[1])
118 near = camera.clipStart
121 scale = float(camera.scale)
123 fovy = atan(0.5/aspect/(camera.lens/32))
124 fovy = fovy * 360.0/pi
126 # What projection do we want?
128 #mP = self._calcOrthoMatrix(fovy, aspect, near, far, 17) #camera.scale)
129 mP = self._calcOrthoMatrix(fovy, aspect, near, far, scale)
131 mP = self._calcPerspectiveMatrix(fovy, aspect, near, far)
133 # View transformation
134 cam = Matrix(cameraObj.getInverseMatrix())
139 self.projectionMatrix = mP
145 def doProjection(self, v):
146 """Project the point on the view plane.
148 Given a vertex calculate the projection using the current projection
152 # Note that we have to work on the vertex using homogeneous coordinates
153 p = self.projectionMatrix * Vector(v).resize4D()
169 def _calcPerspectiveMatrix(self, fovy, aspect, near, far):
170 """Return a perspective projection matrix.
173 top = near * tan(fovy * pi / 360.0)
177 x = (2.0 * near) / (right-left)
178 y = (2.0 * near) / (top-bottom)
179 a = (right+left) / (right-left)
180 b = (top+bottom) / (top - bottom)
181 c = - ((far+near) / (far-near))
182 d = - ((2*far*near)/(far-near))
188 [0.0, 0.0, -1.0, 0.0])
192 def _calcOrthoMatrix(self, fovy, aspect , near, far, scale):
193 """Return an orthogonal projection matrix.
196 # The 11 in the formula was found emiprically
197 top = near * tan(fovy * pi / 360.0) * (scale * 11)
199 left = bottom * aspect
204 tx = -((right+left)/rl)
205 ty = -((top+bottom)/tb)
209 [2.0/rl, 0.0, 0.0, tx],
210 [0.0, 2.0/tb, 0.0, ty],
211 [0.0, 0.0, 2.0/fn, tz],
212 [0.0, 0.0, 0.0, 1.0])
217 # ---------------------------------------------------------------------
219 ## 2DObject representation class
221 # ---------------------------------------------------------------------
223 # TODO: a class to represent the needed properties of a 2D vector image
224 # For now just using a [N]Mesh structure.
227 # ---------------------------------------------------------------------
229 ## Vector Drawing Classes
231 # ---------------------------------------------------------------------
237 A class for printing output in a vectorial format.
239 Given a 2D representation of the 3D scene the class is responsible to
240 write it is a vector format.
242 Every subclasses of VectorWriter must have at last the following public
246 - printCanvas(self, scene,
247 doPrintPolygons=True, doPrintEdges=False, showHiddenEdges=False):
250 def __init__(self, fileName):
251 """Set the output file name and other properties"""
253 self.outputFileName = fileName
256 context = Scene.GetCurrent().getRenderingContext()
257 self.canvasSize = ( context.imageSizeX(), context.imageSizeY() )
261 self.animation = False
268 def open(self, startFrame=1, endFrame=1):
269 if startFrame != endFrame:
270 self.startFrame = startFrame
271 self.endFrame = endFrame
272 self.animation = True
274 self.file = open(self.outputFileName, "w")
275 print "Outputting to: ", self.outputFileName
283 def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False,
284 showHiddenEdges=False):
285 """This is the interface for the needed printing routine.
292 class SVGVectorWriter(VectorWriter):
293 """A concrete class for writing SVG output.
296 def __init__(self, fileName):
297 """Simply call the parent Contructor.
299 VectorWriter.__init__(self, fileName)
306 def open(self, startFrame=1, endFrame=1):
307 """Do some initialization operations.
309 VectorWriter.open(self, startFrame, endFrame)
313 """Do some finalization operation.
317 # remember to call the close method of the parent
318 VectorWriter.close(self)
321 def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False,
322 showHiddenEdges=False):
323 """Convert the scene representation to SVG.
326 Objects = scene.getChildren()
328 context = scene.getRenderingContext()
329 framenumber = context.currentFrame()
332 framestyle = "display:none"
334 framestyle = "display:block"
336 # Assign an id to this group so we can set properties on it using DOM
337 self.file.write("<g id=\"frame%d\" style=\"%s\">\n" %
338 (framenumber, framestyle) )
342 if(obj.getType() != 'Mesh'):
345 self.file.write("<g id=\"%s\">\n" % obj.getName())
347 mesh = obj.getData(mesh=1)
350 self._printPolygons(mesh)
353 self._printEdges(mesh, showHiddenEdges)
355 self.file.write("</g>\n")
357 self.file.write("</g>\n")
364 def _calcCanvasCoord(self, v):
365 """Convert vertex in scene coordinates to canvas coordinates.
368 pt = Vector([0, 0, 0])
370 mW = float(self.canvasSize[0])/2.0
371 mH = float(self.canvasSize[1])/2.0
373 # rescale to canvas size
374 pt[0] = v.co[0]*mW + mW
375 pt[1] = v.co[1]*mH + mH
378 # For now we want (0,0) in the top-left corner of the canvas.
379 # Mirror and translate along y
381 pt[1] += self.canvasSize[1]
385 def _printHeader(self):
386 """Print SVG header."""
388 self.file.write("<?xml version=\"1.0\"?>\n")
389 self.file.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n")
390 self.file.write("\t\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n")
391 self.file.write("<svg version=\"1.1\"\n")
392 self.file.write("\txmlns=\"http://www.w3.org/2000/svg\"\n")
393 self.file.write("\twidth=\"%d\" height=\"%d\" streamable=\"true\">\n\n" %
398 self.file.write("""\n<script><![CDATA[
402 /* FIXME: Use 1000 as interval as lower values gives problems */
403 timerID = setInterval("NextFrame()", 1000);
404 globalFrameCounter=%d;
408 currentElement = document.getElementById('frame'+globalFrameCounter)
409 previousElement = document.getElementById('frame'+(globalFrameCounter-1))
416 if (globalFrameCounter > globalEndFrame)
418 clearInterval(timerID)
424 previousElement.style.display="none";
426 currentElement.style.display="block";
427 globalFrameCounter++;
431 \n""" % (self.startFrame, self.endFrame, self.startFrame) )
433 def _printFooter(self):
434 """Print the SVG footer."""
436 self.file.write("\n</svg>\n")
438 def _printPolygons(self, mesh):
439 """Print the selected (visible) polygons.
442 if len(mesh.faces) == 0:
445 self.file.write("<g>\n")
447 for face in mesh.faces:
451 self.file.write("<polygon points=\"")
454 p = self._calcCanvasCoord(v)
455 self.file.write("%g,%g " % (p[0], p[1]))
457 # get rid of the last blank space, just cosmetics here.
458 self.file.seek(-1, 1)
459 self.file.write("\"\n")
461 # take as face color the first vertex color
462 # TODO: the average of vetrex colors?
465 color = [fcol.r, fcol.g, fcol.b, fcol.a]
467 color = [255, 255, 255, 255]
469 # use the stroke property to alleviate the "adjacent edges" problem,
470 # we simulate polygon expansion using borders,
471 # see http://www.antigrain.com/svg/index.html for more info
475 # Convert the color to the #RRGGBB form
476 str_col = "#%02X%02X%02X" % (color[0], color[1], color[2])
478 self.file.write("\tstyle=\"fill:" + str_col + ";")
479 if POLYGON_EXPANSION_TRICK:
480 self.file.write(" stroke:" + str_col + ";")
481 self.file.write(" stroke-width:" + str(stroke_width) + ";\n")
482 self.file.write(" stroke-linecap:round;stroke-linejoin:round")
483 self.file.write("\"/>\n")
485 self.file.write("</g>\n")
487 def _printEdges(self, mesh, showHiddenEdges=False):
488 """Print the wireframe using mesh edges.
491 stroke_width=EDGES_WIDTH
492 stroke_col = [0, 0, 0]
494 self.file.write("<g>\n")
498 hidden_stroke_style = ""
500 # Consider an edge selected if both vertices are selected
501 if e.v1.sel == 0 or e.v2.sel == 0:
502 if showHiddenEdges == False:
505 hidden_stroke_style = ";\n stroke-dasharray:3, 3"
507 p1 = self._calcCanvasCoord(e.v1)
508 p2 = self._calcCanvasCoord(e.v2)
510 self.file.write("<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"\n"
511 % ( p1[0], p1[1], p2[0], p2[1] ) )
512 self.file.write(" style=\"stroke:rgb("+str(stroke_col[0])+","+str(stroke_col[1])+","+str(stroke_col[2])+");")
513 self.file.write(" stroke-width:"+str(stroke_width)+";\n")
514 self.file.write(" stroke-linecap:round;stroke-linejoin:round")
515 self.file.write(hidden_stroke_style)
516 self.file.write("\"/>\n")
518 self.file.write("</g>\n")
522 # ---------------------------------------------------------------------
526 # ---------------------------------------------------------------------
529 """Render a scene viewed from a given camera.
531 This class is responsible of the rendering process, transformation and
532 projection of the objects in the scene are invoked by the renderer.
534 The rendering is done using the active camera for the current scene.
538 """Make the rendering process only for the current scene by default.
540 We will work on a copy of the scene, be sure that the current scene do
541 not get modified in any way.
544 # Render the current Scene, this should be a READ-ONLY property
545 self._SCENE = Scene.GetCurrent()
547 # Use the aspect ratio of the scene rendering context
548 context = self._SCENE.getRenderingContext()
550 aspect_ratio = float(context.imageSizeX())/float(context.imageSizeY())
551 self.canvasRatio = (float(context.aspectRatioX())*aspect_ratio,
552 float(context.aspectRatioY())
555 # Render from the currently active camera
556 self.cameraObj = self._SCENE.getCurrentCamera()
557 print dir(self._SCENE)
559 # Get the list of lighting sources
560 obj_lst = self._SCENE.getChildren()
561 self.lights = [ o for o in obj_lst if o.getType() == 'Lamp']
563 if len(self.lights) == 0:
565 lobj = Object.New('Lamp')
567 self.lights.append(lobj)
574 def doRendering(self, outputWriter, animation=False):
575 """Render picture or animation and write it out.
578 - a Vector writer object that will be used to output the result.
579 - a flag to tell if we want to render an animation or only the
583 context = self._SCENE.getRenderingContext()
584 currentFrame = context.currentFrame()
586 # Handle the animation case
588 startFrame = currentFrame
589 endFrame = startFrame
592 startFrame = context.startFrame()
593 endFrame = context.endFrame()
594 outputWriter.open(startFrame, endFrame)
596 # Do the rendering process frame by frame
597 print "Start Rendering!"
598 for f in range(startFrame, endFrame+1):
599 context.currentFrame(f)
601 renderedScene = self.doRenderScene(self._SCENE)
602 outputWriter.printCanvas(renderedScene,
603 doPrintPolygons = PRINT_POLYGONS,
604 doPrintEdges = PRINT_EDGES,
605 showHiddenEdges = SHOW_HIDDEN_EDGES)
607 # clear the rendered scene
608 self._SCENE.makeCurrent()
609 Scene.unlink(renderedScene)
614 context.currentFrame(currentFrame)
617 def doRenderScene(self, inputScene):
618 """Control the rendering process.
620 Here we control the entire rendering process invoking the operation
621 needed to transform and project the 3D scene in two dimensions.
624 # Use some temporary workspace, a full copy of the scene
625 workScene = inputScene.copy(2)
627 # Get a projector for this scene.
628 # NOTE: the projector wants object in world coordinates,
629 # so we should apply modelview transformations _before_
630 # projection transformations
631 proj = Projector(self.cameraObj, self.canvasRatio)
633 # global processing of the scene
635 self._doConvertGeometricObjToMesh(workScene)
637 self._doSceneClipping(workScene)
639 # FIXME: does not work in batch mode!
640 #if OPTIMIZE_FOR_SPACE:
641 # self._joinMeshObjectsInScene(workScene)
643 self._doSceneDepthSorting(workScene)
645 # Per object activities
647 Objects = workScene.getChildren()
650 if obj.getType() != 'Mesh':
651 print "Only Mesh supported! - Skipping type:", obj.getType()
654 print "Rendering: ", obj.getName()
658 self._doModelToWorldCoordinates(mesh, obj.matrix)
660 self._doObjectDepthSorting(mesh)
662 self._doBackFaceCulling(mesh)
664 self._doColorAndLighting(mesh)
666 # TODO: 'style' can be a function that determine
667 # if an edge should be showed?
668 self._doEdgesStyle(mesh, style=None)
670 self._doProjection(mesh, proj)
672 # Update the object data, important! :)
684 def _getObjPosition(self, obj):
685 """Return the obj position in World coordinates.
687 return obj.matrix.translationPart()
689 def _cameraViewDirection(self):
690 """Get the View Direction form the camera matrix.
692 return Vector(self.cameraObj.matrix[2]).resize3D()
697 def _isFaceVisible(self, face):
698 """Determine if a face of an object is visible from the current camera.
700 The view vector is calculated from the camera location and one of the
701 vertices of the face (expressed in World coordinates, after applying
702 modelview transformations).
704 After those transformations we determine if a face is visible by
705 computing the angle between the face normal and the view vector, this
706 angle has to be between -90 and 90 degrees for the face to be visible.
707 This corresponds somehow to the dot product between the two, if it
708 results > 0 then the face is visible.
710 There is no need to normalize those vectors since we are only interested in
711 the sign of the cross product and not in the product value.
713 NOTE: here we assume the face vertices are in WorldCoordinates, so
714 please transform the object _before_ doing the test.
717 normal = Vector(face.no)
718 camPos = self._getObjPosition(self.cameraObj)
721 # View Vector in orthographics projections is the view Direction of
723 if self.cameraObj.data.getType() == 1:
724 view_vect = self._cameraViewDirection()
726 # View vector in perspective projections can be considered as
727 # the difference between the camera position and one point of
728 # the face, we choose the farthest point from the camera.
729 if self.cameraObj.data.getType() == 0:
730 vv = max( [ ((camPos - Vector(v.co)).length, (camPos - Vector(v.co))) for v in face] )
733 # if d > 0 the face is visible from the camera
734 d = view_vect * normal
744 def _doConvertGeometricObjToMesh(self, scene):
745 """Convert all "geometric" objects to mesh ones.
747 geometricObjTypes = ['Mesh', 'Surf', 'Curve', 'Text']
749 Objects = scene.getChildren()
750 objList = [ o for o in Objects if o.getType() in geometricObjTypes ]
753 obj = self._convertToRawMeshObj(obj)
755 scene.unlink(old_obj)
758 me = obj.getData(mesh=1)
759 for f in me.faces: f.sel = 1;
760 for v in me.verts: v.sel = 1;
766 def _doSceneClipping(self, scene):
767 """Clip objects against the View Frustum.
769 For now clip away only objects according to their center position.
772 cpos = self._getObjPosition(self.cameraObj)
773 view_vect = self._cameraViewDirection()
775 near = self.cameraObj.data.clipStart
776 far = self.cameraObj.data.clipEnd
778 aspect = float(self.canvasRatio[0])/float(self.canvasRatio[1])
779 fovy = atan(0.5/aspect/(self.cameraObj.data.lens/32))
780 fovy = fovy * 360.0/pi
782 Objects = scene.getChildren()
784 if o.getType() != 'Mesh': continue;
786 obj_vect = Vector(cpos) - self._getObjPosition(o)
788 d = obj_vect*view_vect
789 theta = AngleBetweenVecs(obj_vect, view_vect)
791 # if the object is outside the view frustum, clip it away
792 if (d < near) or (d > far) or (theta > fovy):
795 def _doSceneDepthSorting(self, scene):
796 """Sort objects in the scene.
798 The object sorting is done accordingly to the object centers.
801 c = self._getObjPosition(self.cameraObj)
803 by_center_pos = (lambda o1, o2:
804 (o1.getType() == 'Mesh' and o2.getType() == 'Mesh') and
805 cmp((self._getObjPosition(o1) - Vector(c)).length,
806 (self._getObjPosition(o2) - Vector(c)).length)
809 # TODO: implement sorting by bounding box, if obj1.bb is inside obj2.bb,
810 # then ob1 goes farther than obj2, useful when obj2 has holes
813 Objects = scene.getChildren()
814 Objects.sort(by_center_pos)
821 def _joinMeshObjectsInScene(self, scene):
822 """Merge all the Mesh Objects in a scene into a single Mesh Object.
825 bigObj = Object.New('Mesh', 'BigOne')
828 oList = [o for o in scene.getChildren() if o.getType()=='Mesh']
839 def _convertToRawMeshObj(self, object):
840 """Convert geometry based object to a mesh object.
842 me = Mesh.New('RawMesh_'+object.name)
843 me.getFromObject(object.name)
845 newObject = Object.New('Mesh', 'RawMesh_'+object.name)
848 # If the object has no materials set a default material
850 me.materials = [Material.New()]
851 #for f in me.faces: f.mat = 0
853 newObject.setMatrix(object.getMatrix())
857 def _doModelToWorldCoordinates(self, mesh, matrix):
858 """Transform object coordinates to world coordinates.
860 This step is done simply applying to the object its tranformation
861 matrix and recalculating its normals.
863 mesh.transform(matrix, True)
865 def _doObjectDepthSorting(self, mesh):
866 """Sort faces in an object.
868 The faces in the object are sorted following the distance of the
869 vertices from the camera position.
871 c = self._getObjPosition(self.cameraObj)
873 # hackish sorting of faces
875 # Sort faces according to the max distance from the camera
876 by_max_vert_dist = (lambda f1, f2:
877 cmp(max([(Vector(v.co)-Vector(c)).length for v in f1]),
878 max([(Vector(v.co)-Vector(c)).length for v in f2])))
880 # Sort faces according to the min distance from the camera
881 by_min_vert_dist = (lambda f1, f2:
882 cmp(min([(Vector(v.co)-Vector(c)).length for v in f1]),
883 min([(Vector(v.co)-Vector(c)).length for v in f2])))
885 # Sort faces according to the avg distance from the camera
886 by_avg_vert_dist = (lambda f1, f2:
887 cmp(sum([(Vector(v.co)-Vector(c)).length for v in f1])/len(f1),
888 sum([(Vector(v.co)-Vector(c)).length for v in f2])/len(f2)))
890 mesh.faces.sort(by_max_vert_dist)
893 def _doBackFaceCulling(self, mesh):
894 """Simple Backface Culling routine.
896 At this level we simply do a visibility test face by face and then
897 select the vertices belonging to visible faces.
900 # Select all vertices, so edges can be displayed even if there are no
905 Mesh.Mode(Mesh.SelectModes['FACE'])
909 if self._isFaceVisible(f):
912 # Is this the correct way to propagate the face selection info to the
913 # vertices belonging to a face ??
914 # TODO: Using the Mesh module this should come for free. Right?
915 Mesh.Mode(Mesh.SelectModes['VERTEX'])
918 for v in f: v.sel = 0;
922 for v in f: v.sel = 1;
924 def _doColorAndLighting(self, mesh):
925 """Apply an Illumination model to the object.
927 The Illumination model used is the Phong one, it may be inefficient,
928 but I'm just learning about rendering and starting from Phong seemed
929 the most natural way.
932 # If the mesh has vertex colors already, use them,
933 # otherwise turn them on and do some calculations
934 if mesh.hasVertexColours():
936 mesh.hasVertexColours(True)
938 materials = mesh.materials
940 # TODO: use multiple lighting sources
941 light_obj = self.lights[0]
942 light_pos = self._getObjPosition(light_obj)
943 light = light_obj.data
945 camPos = self._getObjPosition(self.cameraObj)
947 # We do per-face color calculation (FLAT Shading), we can easily turn
948 # to a per-vertex calculation if we want to implement some shading
949 # technique. For an example see:
950 # http://www.miralab.unige.ch/papers/368.pdf
957 mat = materials[f.mat]
959 # A new default material
961 mat = Material.New('defMat')
963 L = Vector(light_pos).normalize()
965 V = (Vector(camPos) - Vector(f.v[0].co)).normalize()
967 N = Vector(f.no).normalize()
969 R = 2 * (N*L) * N - L
971 # TODO: Attenuation factor (not used for now)
972 a0 = 1; a1 = 0.0; a2 = 0.0
973 d = (Vector(f.v[0].co) - Vector(light_pos)).length
974 fd = min(1, 1.0/(a0 + a1*d + a2*d*d))
978 ka = mat.getAmb() * Vector([0.1, 0.1, 0.1])
981 # Diffuse component (add light.col for kd)
982 kd = mat.getRef() * Vector(mat.getRGBCol())
983 Ip = light.getEnergy()
984 Idiff = Ip * kd * (N*L)
987 ks = mat.getSpec() * Vector(mat.getSpecCol())
988 ns = mat.getHardness()
989 Ispec = Ip * ks * pow((V * R), ns)
992 ki = Vector([mat.getEmit()]*3)
994 I = ki + Iamb + Idiff + Ispec
996 # Clamp I values between 0 and 1
997 I = [ min(c, 1) for c in I]
998 I = [ max(0, c) for c in I]
999 tmp_col = [ int(c * 255.0) for c in I]
1001 vcol = NMesh.Col(tmp_col[0], tmp_col[1], tmp_col[2], 255)
1006 def _doEdgesStyle(self, mesh, style):
1007 """Process Mesh Edges.
1009 Examples of algorithms:
1012 given an edge if its adjacent faces have the same normal (that is
1013 they are complanar), than deselect it.
1016 given an edge if one its adjacent faces is frontfacing and the
1017 other is backfacing, than select it, else deselect.
1019 #print "\tTODO: _doEdgeStyle()"
1022 def _doProjection(self, mesh, projector):
1023 """Calculate the Projection for the object.
1025 # TODO: maybe using the object.transform() can be faster?
1027 for v in mesh.verts:
1028 p = projector.doProjection(v.co)
1035 # ---------------------------------------------------------------------
1039 # ---------------------------------------------------------------------
1041 def vectorize(filename):
1042 """The vectorizing process is as follows:
1044 - Instanciate the writer and the renderer
1047 from Blender import Window
1048 editmode = Window.EditMode()
1049 if editmode: Window.EditMode(0)
1051 writer = SVGVectorWriter(filename)
1053 renderer = Renderer()
1054 renderer.doRendering(writer, RENDER_ANIMATION)
1056 if editmode: Window.EditMode(1)
1058 def vectorize_gui(filename):
1061 I would like to keep that simple, really.
1063 Blender.Window.FileSelector (vectorize, 'Save SVG', filename)
1068 if __name__ == "__main__":
1070 basename = Blender.sys.basename(Blender.Get('filename'))
1071 outputfile = Blender.sys.splitext(basename)[0]+".svg"
1073 # with this trick we can run the script in batch mode
1075 vectorize_gui(outputfile)
1077 vectorize(outputfile)