[Bf-extensions-cvs] [36d55fc] master: Fix T43635: 'fill contours' fails when using multiple render layers There was a problem with the order of stroke and fill elements. Additionally, sometimes names of linesets collide between render layers. this has been fixed by prepending the name of the render layer to a layer's id.

Folkert de Vries noreply at git.blender.org
Wed Feb 18 19:27:37 CET 2015


Commit: 36d55fccfc1e9aa2ba8824f2f0241704ce1759b6
Author: Folkert de Vries
Date:   Wed Feb 18 19:22:10 2015 +0100
Branches: master
https://developer.blender.org/rBA36d55fccfc1e9aa2ba8824f2f0241704ce1759b6

Fix T43635:  'fill contours' fails when using multiple render layers
There was a problem with the order of stroke and fill elements. Additionally, sometimes names of linesets collide between render layers. this has been fixed by prepending the name of the render layer to a layer's id.

===================================================================

M	render_freestyle_svg.py

===================================================================

diff --git a/render_freestyle_svg.py b/render_freestyle_svg.py
index 6423847..4926724 100644
--- a/render_freestyle_svg.py
+++ b/render_freestyle_svg.py
@@ -63,6 +63,7 @@ from bpy.props import (
         )
 from bpy.app.handlers import persistent
 from collections import OrderedDict
+from functools import partial
 from mathutils import Vector
 
 
@@ -78,6 +79,14 @@ namespaces = {
     "svg": "http://www.w3.org/2000/svg",
     }
 
+# wrap XMLElem.find, so the namespaces don't need to be given as an argument
+def find_xml_elem(obj, search, namespaces, *, all=False):
+    if all:
+        return obj.findall(search, namespaces=namespaces)
+    return obj.find(search, namespaces=namespaces)
+
+find_svg_elem = partial(find_xml_elem, namespaces=namespaces)
+
 
 def render_height(scene):
     return int(scene.render.resolution_y * scene.render.resolution_percentage / 100)
@@ -87,6 +96,7 @@ def render_width(scene):
     return int(scene.render.resolution_x * scene.render.resolution_percentage / 100)
 
 
+# stores the state of the render, used to differ between animation and single frame renders.
 class RenderState:
     # Note that this flag is set to False only after the first frame
     # has been written to file.
@@ -213,10 +223,10 @@ def write_animation(filepath, frame_begin, fps):
     tree = et.parse(filepath)
     root = tree.getroot()
 
-    linesets = tree.findall(".//svg:g[@inkscape:groupmode='lineset']", namespaces=namespaces)
+    linesets = find_svg_elem(tree, ".//svg:g[@inkscape:groupmode='lineset']", all=True)
     for i, lineset in enumerate(linesets):
         name = lineset.get('id')
-        frames = lineset.findall(".//svg:g[@inkscape:groupmode='frame']", namespaces=namespaces)
+        frames = find_svg_elem(lineset, ".//svg:g[@inkscape:groupmode='frame']", all=True)
         n_of_frames = len(frames)
         keyTimes = ";".join(str(round(x / n_of_frames, 3)) for x in range(n_of_frames)) + ";1"
 
@@ -313,8 +323,9 @@ class SVGPathShader(StrokeShader):
         name = self._name
         scene = bpy.context.scene
 
-        # make <g> for lineset as a whole (don't overwrite)
-        lineset_group = tree.find(".//svg:g[@id='{}']".format(name), namespaces=namespaces)
+        # create <g> for lineset as a whole (don't overwrite)
+        # when rendering an animation, frames will be nested in here, otherwise a group of strokes and optionally fills.
+        lineset_group = find_svg_elem(tree, ".//svg:g[@id='{}']".format(name))
         if lineset_group is None:
             lineset_group = et.XML('<g/>')
             lineset_group.attrib = {
@@ -323,15 +334,11 @@ class SVGPathShader(StrokeShader):
                 'inkscape:groupmode': 'lineset',
                 'inkscape:label': name,
                 }
-            root.insert(0, lineset_group)
+            root.append(lineset_group)
 
-        # make <g> for the current frame
+        # create <g> for the current frame
         id = "frame_{:04n}".format(self.frame_current)
 
-        if scene.svg_export.mode == 'ANIMATION':
-            frame_group = et.XML("<g/>")
-            frame_group.attrib = {'id': id, 'inkscape:groupmode': 'frame', 'inkscape:label': id}
-
         stroke_group = et.XML("<g/>")
         stroke_group.attrib = {'xmlns:inkscape': namespaces["inkscape"],
                                'inkscape:groupmode': 'layer',
@@ -340,13 +347,15 @@ class SVGPathShader(StrokeShader):
         # nest the structure
         stroke_group.extend(self.elements)
         if scene.svg_export.mode == 'ANIMATION':
+            frame_group = et.XML("<g/>")
+            frame_group.attrib = {'id': id, 'inkscape:groupmode': 'frame', 'inkscape:label': id}
             frame_group.append(stroke_group)
             lineset_group.append(frame_group)
         else:
             lineset_group.append(stroke_group)
 
         # write SVG to file
-        print("SVG Export: writing to ", self.filepath)
+        print("SVG Export: writing to", self.filepath)
         indent_xml(root)
         tree.write(self.filepath, encoding='ascii', xml_declaration=True)
 
@@ -391,6 +400,7 @@ class SVGFillShader(StrokeShader):
         root = tree.getroot()
         name = self._name
         scene = bpy.context.scene
+        lineset_group = find_svg_elem(tree, ".//svg:g[@id='{}']".format(name))
 
         # create XML elements from the acquired data
         elems = []
@@ -400,39 +410,30 @@ class SVGFillShader(StrokeShader):
             for stroke in strokes:
                 elems.append(et.XML("".join(self.pathgen((sv.point for sv in stroke), p, self.h))))
 
-        # make <g> for lineset as a whole (don't overwrite)
-        lineset_group = tree.find(".//svg:g[@id='{}']".format(name), namespaces=namespaces)
-        if lineset_group is None:
-            lineset_group = et.XML('<g/>')
-            lineset_group.attrib = {
-                'id': name,
-                'xmlns:inkscape': namespaces["inkscape"],
-                'inkscape:groupmode': 'lineset',
-                'inkscape:label': name,
-                }
-            root.insert(0, lineset_group)
-
         if scene.svg_export.mode == 'ANIMATION':
             # add the fills to the <g> of the current frame
-            frame_group = tree.find(".//svg:g[@id='frame_{:04n}']".format(scene.frame_current), namespaces=namespaces)
+            frame_group = find_svg_elem(lineset_group, ".//svg:g[@id='frame_{:04n}']".format(scene.frame_current))
             if frame_group is None:
                 # something has gone very wrong
                 raise RuntimeError("SVGFillShader: frame_group is None")
 
-        # add <g> for the strokes of the current frame
-        stroke_group = et.XML("<g/>")
-        stroke_group.attrib = {'xmlns:inkscape': namespaces["inkscape"],
-                               'inkscape:groupmode': 'layer',
-                               'inkscape:label': 'fills',
-                               'id': 'fills'}
 
-        # reverse fills to get the correct order
-        stroke_group.extend(reversed(elems))
+        # <g> for the fills of the current frame
+        fill_group = et.XML('<g/>')
+        fill_group.attrib = {
+            'xmlns:inkscape': namespaces["inkscape"],
+           'inkscape:groupmode': 'layer',
+           'inkscape:label': 'fills',
+           'id': 'fills'
+           }
 
+        fill_group.extend(reversed(elems))
         if scene.svg_export.mode == 'ANIMATION':
-            frame_group.insert(0, stroke_group)
+            frame_group.insert(0, fill_group)
         else:
-            lineset_group.insert(0, stroke_group)
+            # get the current lineset group. if it's None we're in trouble, so may as well error hard.
+            lineset_group = tree.find(".//svg:g[@id='{}']".format(name), namespaces=namespaces)
+            lineset_group.insert(0, fill_group)
 
         # write SVG to file
         indent_xml(root)
@@ -461,7 +462,7 @@ class SVGPathShaderCallback(ParameterEditorCallback):
         split = scene.svg_export.split_at_invisible
         cls.shader = SVGPathShader.from_lineset(
                 lineset, create_path(scene),
-                render_height(scene), split, scene.frame_current)
+                render_height(scene), split, scene.frame_current, name=layer.name + '_' + lineset.name)
         return [cls.shader]
 
     @classmethod
@@ -489,8 +490,9 @@ class SVGFillShaderCallback(ParameterEditorCallback):
         # sort according to the distance from camera
         Operators.sort(pyZBP1D())
         # render and write fills
-        shader = SVGFillShader(create_path(scene), render_height(scene), lineset.name)
+        shader = SVGFillShader(create_path(scene), render_height(scene), layer.name + '_' + lineset.name)
         Operators.create(TrueUP1D(), [shader, ])
+
         shader.write()
 
 
@@ -559,3 +561,4 @@ def unregister():
 
 if __name__ == "__main__":
     register()
+



More information about the Bf-extensions-cvs mailing list