From 70f6a782883f14028b00ca6b2cfbc8d57469757f Mon Sep 17 00:00:00 2001 From: "Michael X. Grey" Date: Tue, 8 May 2018 13:22:19 -0700 Subject: [PATCH] Implementing a depth-first flattening of groups --- svgpathtools/document.py | 46 ++++++++++++++++++++++++++++++++++----- svgpathtools/svg2paths.py | 2 ++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/svgpathtools/document.py b/svgpathtools/document.py index 21413b4..67baa6d 100644 --- a/svgpathtools/document.py +++ b/svgpathtools/document.py @@ -51,19 +51,22 @@ A Big Problem: # External dependencies from __future__ import division, absolute_import, print_function import os +import collections import xml.etree.cElementTree as etree import xml.etree.ElementTree.Element as Element import xml.etree.ElementTree.SubElement as SubElement # Internal dependencies from .parser import parse_path -from .svg2paths import (ellipse2pathd, line2pathd, polyline2pathd, +from .parser import parse_transform +from .svg2paths import (path2pathd, ellipse2pathd, line2pathd, polyline2pathd, polygon2pathd, rect2pathd) from .misctools import open_in_browser from .path import * -# THESE MUST BE WRAPPED TO OUPUT ElementTree.element objects -CONVERSIONS = {'circle': ellipse2pathd, +# THESE MUST BE WRAPPED TO OUTPUT ElementTree.element objects +CONVERSIONS = {'path': path2pathd, + 'circle': ellipse2pathd, 'ellipse': ellipse2pathd, 'line': line2pathd, 'polyline': polyline2pathd, @@ -71,11 +74,44 @@ CONVERSIONS = {'circle': ellipse2pathd, 'rect': rect2pathd} -def flatten_group_transforms(group): - """Returns a 3x3 matrix which can transform points on a path from a group frame to the root frame""" +def flatten_paths(group, return_attribs = False, group_filter = lambda x: True, path_filter = lambda x: True, + path_conversions = CONVERSIONS): + """Returns the paths inside a group (recursively), expressing the paths in the root coordinates + + @param group is an Element""" if not isinstance(group, Element): raise TypeError('Must provide an xml.etree.Element object') + # Stop right away if the group_selector rejects this group + if not group_filter(group): + return [] + + def get_relevant_children(parent): + return filter(group_filter, parent.findall('g')) + + # To handle the transforms efficiently, we'll traverse the tree of groups depth-first using a stack of tuples. + # The first entry in the tuple is the group element, the second entry is its transform, the third is its + # list of child elements, and the fourth is the index of the next child to traverse for that element. + StackElement = collections.namedtuple('StackElement', ['group', 'transform', 'children', 'next_child_index']) + + def new_stack_element(element): + return StackElement(element, parse_transform(element.get('transform')), get_relevant_children(element), 0) + + stack = [new_stack_element(group)] + + paths = [] + if return_attribs: path_attribs = [] + + while stack: + top = stack[-1] + + for key in path_conversions: + for path_elem in filter(path_filter, top.group.iterfind(key)): + pass # TODO: Finish this + + + + diff --git a/svgpathtools/svg2paths.py b/svgpathtools/svg2paths.py index 1b55337..eab6790 100644 --- a/svgpathtools/svg2paths.py +++ b/svgpathtools/svg2paths.py @@ -9,6 +9,8 @@ import xml.etree.cElementTree as etree # Internal dependencies from .parser import parse_path +def path2pathd(path): + return path.get('d', None) def ellipse2pathd(ellipse): """converts the parameters from an ellipse or a circle to a string