PEP8 and fix in unittest

pull/23/head
alphanoob1337 2017-05-17 12:26:38 +02:00
parent 77cab1e819
commit ecdade1be3
5 changed files with 808 additions and 699 deletions

View File

@ -2136,7 +2136,13 @@ class Path(MutableSequence):
def cropped(self, T0, T1):
"""returns a cropped copy of the path."""
assert 0 <= T0 <= 1 and 0 <= T1<= 1
assert T0 != T1
assert not (T0 == 1 and T1 == 0)
if T0 == 1 and 0 < T1 < 1 and self.isclosed():
return self.cropped(0, T1)
if T1 == 1:
seg1 = self[-1]
t_seg1 = 1
@ -2171,7 +2177,7 @@ class Path(MutableSequence):
# T1<T0 must cross discontinuity case
if T1 < T0:
if self.isclosed():
if not self.isclosed():
raise ValueError("This path is not closed, thus T0 must "
"be less than T1.")
else:

View File

@ -5,23 +5,21 @@ The main tool being the svg2paths() function."""
from __future__ import division, absolute_import, print_function
from xml.dom.minidom import parse
from os import path as os_path, getcwd
from shutil import copyfile
import numpy as np
# Internal dependencies
from .parser import parse_path
from .path import Path, bpoints2bezier
def polyline2pathd(polyline_d):
"""converts the string from a polyline d-attribute to a string for a Path
object d-attribute"""
"""converts the string from a polyline points-attribute to a string for a
Path object d-attribute"""
points = polyline_d.replace(', ', ',')
points = points.replace(' ,', ',')
points = points.split()
if points[0] == points[-1]:
closed = True
else:
closed = False
closed = points[0] == points[-1]
d = 'M' + points.pop(0).replace(',', ' ')
for p in points:
@ -31,6 +29,30 @@ def polyline2pathd(polyline_d):
return d
def polygon2pathd(polyline_d):
"""converts the string from a polygon points-attribute to a string for a
Path object d-attribute.
Note: For a polygon made from n points, the resulting path will be
composed of n lines (even if some of these lines have length zero)."""
points = polyline_d.replace(', ', ',')
points = points.replace(' ,', ',')
points = points.split()
reduntantly_closed = points[0] == points[-1]
d = 'M' + points[0].replace(',', ' ')
for p in points[1:]:
d += 'L' + p.replace(',', ' ')
# The `parse_path` call ignores redundant 'z' (closure) commands
# e.g. `parse_path('M0 0L100 100Z') == parse_path('M0 0L100 100L0 0Z')`
# This check ensures that an n-point polygon is converted to an n-Line path.
if reduntantly_closed:
d += 'L' + points[0].replace(',', ' ')
return d + 'z'
def svg2paths(svg_file_location,
convert_lines_to_paths=True,
convert_polylines_to_paths=True,
@ -62,52 +84,126 @@ def svg2paths(svg_file_location,
# else:
doc = parse(svg_file_location)
# Parse a list of paths
def dom2dict(element):
"""Converts DOM elements to dictionaries of attributes."""
keys = list(element.attributes.keys())
values = [val.value for val in list(element.attributes.values())]
return dict(list(zip(keys, values)))
# Use minidom to extract path strings from input SVG
paths = [dom2dict(el) for el in doc.getElementsByTagName('path')]
d_strings = [el['d'] for el in paths]
attribute_dictionary_list = paths
# if pathless_svg:
# for el in doc.getElementsByTagName('path'):
# el.parentNode.removeChild(el)
def parse_trafo(trafo_str):
"""Returns six matrix elements for a matrix transformation for any valid SVG transformation string."""
trafos = trafo_str.split(')')[:-1]
trafo_matrix = np.array([1., 0., 0., 0., 1., 0., 0., 0., 1.]).reshape((3, 3)) # Start with neutral matrix
# Use minidom to extract polyline strings from input SVG, convert to
# path strings, add to list
if convert_polylines_to_paths:
plins = [dom2dict(el) for el in doc.getElementsByTagName('polyline')]
d_strings += [polyline2pathd(pl['points']) for pl in plins]
attribute_dictionary_list += plins
for trafo_sub_str in trafos:
trafo_sub_str = trafo_sub_str.lstrip(', ')
value_str = trafo_sub_str.split('(')[1]
values = list(map(float, value_str.split(',')))
if 'translate' in trafo_sub_str:
x = values[0]
y = values[1] if (len(values) > 1) else 0.
trafo_matrix = np.dot(trafo_matrix,
np.array([1., 0., x, 0., 1., y, 0., 0., 1.]).reshape((3, 3)))
elif 'scale' in trafo_sub_str:
x = values[0]
y = values[1] if (len(values) > 1) else 0.
trafo_matrix = np.dot(trafo_matrix,
np.array([x, 0., 0., 0., y, 0., 0., 0., 1.]).reshape((3, 3)))
elif 'rotate' in trafo_sub_str:
a = values[0]*np.pi/180.
x = values[1] if (len(values) > 1) else 0.
y = values[2] if (len(values) > 2) else 0.
am = np.dot(np.array([np.cos(a), -np.sin(a), 0., np.sin(a), np.cos(a), 0., 0., 0., 1.]).reshape((3, 3)),
np.array([1., 0., -x, 0., 1., -y, 0., 0., 1.]).reshape((3, 3)))
am = np.dot(np.array([1., 0., x, 0., 1., y, 0., 0., 1.]).reshape((3, 3)), am)
trafo_matrix = np.dot(trafo_matrix, am)
elif 'skewX' in trafo_sub_str:
a = values[0]*np.pi/180.
trafo_matrix = np.dot(trafo_matrix,
np.array([1., np.tan(a), 0., 0., 1., 0., 0., 0., 1.]).reshape((3, 3)))
elif 'skewY' in trafo_sub_str:
a = values[0]*np.pi/180.
trafo_matrix = np.dot(trafo_matrix,
np.array([1., 0., 0., np.tan(a), 1., 0., 0., 0., 1.]).reshape((3, 3)))
else: # Assume matrix transformation
while len(values) < 6:
values += [0.]
trafo_matrix = np.dot(trafo_matrix,
np.array([values[::2], values[1::2], [0., 0., 1.]]))
# Use minidom to extract polygon strings from input SVG, convert to
# path strings, add to list
if convert_polygons_to_paths:
pgons = [dom2dict(el) for el in doc.getElementsByTagName('polygon')]
d_strings += [polyline2pathd(pg['points']) + 'z' for pg in pgons]
attribute_dictionary_list += pgons
trafo_list = list(trafo_matrix.reshape((9,))[:6])
return trafo_list[::3]+trafo_list[1::3]+trafo_list[2::3]
if convert_lines_to_paths:
lines = [dom2dict(el) for el in doc.getElementsByTagName('line')]
d_strings += [('M' + l['x1'] + ' ' + l['y1'] +
'L' + l['x2'] + ' ' + l['y2']) for l in lines]
attribute_dictionary_list += lines
def parse_node(node):
"""Recursively iterate over nodes. Parse the groups individually to apply group transformations."""
# Get everything in this tag
data = [parse_node(child) for child in node.childNodes]
if len(data) == 0:
ret_list = []
attribute_dictionary_list_int = []
else:
# Flatten the lists
ret_list = []
attribute_dictionary_list_int = []
for item in data:
if type(item) == tuple:
if len(item[0]) > 0:
ret_list += item[0]
attribute_dictionary_list_int += item[1]
# if pathless_svg:
# with open(pathless_svg, "wb") as f:
# doc.writexml(f)
if node.nodeName == 'g':
# Group found
# Analyse group properties
group = dom2dict(node)
if 'transform' in group.keys():
trafo = group['transform']
# Convert all transformations into a matrix operation
am = parse_trafo(trafo)
am = np.array([am[::2], am[1::2], [0., 0., 1.]])
# Apply transformation to all elements of the paths
def xy(p):
return np.array([p.real, p.imag, 1.])
def z(coords):
return coords[0] + 1j*coords[1]
ret_list = [Path(*[bpoints2bezier([z(np.dot(am, xy(pt)))
for pt in seg.bpoints()])
for seg in path])
for path in ret_list]
return ret_list, attribute_dictionary_list_int
elif node.nodeName == 'path':
# Path found; parsing it
path = dom2dict(node)
d_string = path['d']
return [parse_path(d_string)]+ret_list, [path]+attribute_dictionary_list_int
elif convert_polylines_to_paths and node.nodeName == 'polyline':
attrs = dom2dict(node)
path = parse_path(polyline2pathd(node['points']))
return [path]+ret_list, [attrs]+attribute_dictionary_list_int
elif convert_polygons_to_paths and node.nodeName == 'polygon':
attrs = dom2dict(node)
path = parse_path(polygon2pathd(attrs['points']))
return [path]+ret_list, [attrs]+attribute_dictionary_list_int
elif convert_lines_to_paths and node.nodeName == 'line':
line = dom2dict(node)
d_string = ('M' + line['x1'] + ' ' + line['y1'] +
'L' + line['x2'] + ' ' + line['y2'])
path = parse_path(d_string)
return [path]+ret_list, [line]+attribute_dictionary_list_int
else:
return ret_list, attribute_dictionary_list_int
path_list, attribute_dictionary_list = parse_node(doc)
if return_svg_attributes:
svg_attributes = dom2dict(doc.getElementsByTagName('svg')[0])
doc.unlink()
path_list = [parse_path(d) for d in d_strings]
return path_list, attribute_dictionary_list, svg_attributes
else:
doc.unlink()
path_list = [parse_path(d) for d in d_strings]
return path_list, attribute_dictionary_list

View File

@ -1,13 +1,14 @@
Metadata-Version: 1.1
Name: svgpathtools
Version: 1.3.1
Version: 1.3.2b0
Summary: A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.
Home-page: https://github.com/mathandy/svgpathtools
Author: Andy Port
Author-email: AndyAPort@gmail.com
License: MIT
Download-URL: http://github.com/mathandy/svgpathtools/tarball/1.3.1
Description: svgpathtools
Download-URL: http://github.com/mathandy/svgpathtools/tarball/1.3.2beta
Description:
svgpathtools
============
svgpathtools is a collection of tools for manipulating and analyzing SVG
@ -46,11 +47,6 @@ Description: svgpathtools
- compute **inverse arc length**
- convert RGB color tuples to hexadecimal color strings and back
Note on Python 3
----------------
While I am hopeful that this package entirely works with Python 3, it was born from a larger project coded in Python 2 and has not been thoroughly tested in
Python 3. Please let me know if you find any incompatibilities.
Prerequisites
-------------
@ -94,8 +90,6 @@ Description: svgpathtools
module <https://github.com/regebro/svg.path>`__. Interested svg.path
users should see the compatibility notes at bottom of this readme.
Also, a big thanks to the author(s) of `A Primer on Bézier Curves <http://pomax.github.io/bezierinfo/>`_, an outstanding resource for learning about Bézier curves and Bézier curve-related algorithms.
Basic Usage
-----------
@ -126,11 +120,11 @@ Description: svgpathtools
on discontinuous Path objects. A simple workaround is provided, however,
by the ``Path.continuous_subpaths()`` method. `↩ <#a1>`__
.. code:: python
.. code:: ipython2
from __future__ import division, print_function
.. code:: python
.. code:: ipython2
# Coordinates are given as points in the complex plane
from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc
@ -167,7 +161,7 @@ Description: svgpathtools
list. So segments can **append**\ ed, **insert**\ ed, set by index,
**del**\ eted, **enumerate**\ d, **slice**\ d out, etc.
.. code:: python
.. code:: ipython2
# Let's append another to the end of it
path.append(CubicBezier(250+350j, 275+350j, 250+225j, 200+100j))
@ -234,7 +228,7 @@ Description: svgpathtools
| Note: Line, Polyline, Polygon, and Path SVG elements can all be
converted to Path objects using this function.
.. code:: python
.. code:: ipython2
# Read SVG into a list of path objects and list of dictionaries of attributes
from svgpathtools import svg2paths, wsvg
@ -271,7 +265,7 @@ Description: svgpathtools
automatically attempt to open the created svg file in your default SVG
viewer.
.. code:: python
.. code:: ipython2
# Let's make a new SVG that's identical to the first
wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename='output1.svg')
@ -303,7 +297,7 @@ Description: svgpathtools
that ``path.point(T)=path[k].point(t)``.
| There is also a ``Path.t2T()`` method to solve the inverse problem.
.. code:: python
.. code:: ipython2
# Example:
@ -333,11 +327,11 @@ Description: svgpathtools
True
Tangent vectors and Bezier curves as numpy polynomial objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bezier curves as NumPy polynomial objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Another great way to work with the parameterizations for Line,
QuadraticBezier, and CubicBezier objects is to convert them to
| Another great way to work with the parameterizations for ``Line``,
``QuadraticBezier``, and ``CubicBezier`` objects is to convert them to
``numpy.poly1d`` objects. This is done easily using the
``Line.poly()``, ``QuadraticBezier.poly()`` and ``CubicBezier.poly()``
methods.
@ -369,9 +363,10 @@ Description: svgpathtools
\end{bmatrix}
\begin{bmatrix}P_0\\P_1\\P_2\\P_3\end{bmatrix}
QuadraticBezier.poly() and Line.poly() are defined similarly.
``QuadraticBezier.poly()`` and ``Line.poly()`` are `defined
similarly <https://en.wikipedia.org/wiki/B%C3%A9zier_curve#General_definition>`__.
.. code:: python
.. code:: ipython2
# Example:
b = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j)
@ -401,15 +396,25 @@ Description: svgpathtools
(-400 + -100j) t + (900 + 300j) t - 600 t + (300 + 100j)
To illustrate the awesomeness of being able to convert our Bezier curve
objects to numpy.poly1d objects and back, lets compute the unit tangent
vector of the above CubicBezier object, b, at t=0.5 in four different
ways.
The ability to convert between Bezier objects to NumPy polynomial
objects is very useful. For starters, we can take turn a list of Bézier
segments into a NumPy array
Tangent vectors (and more on polynomials)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Numpy Array operations on Bézier path segments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
`Example available
here <https://github.com/mathandy/svgpathtools/blob/master/examples/compute-many-points-quickly-using-numpy-arrays.py>`__
To further illustrate the power of being able to convert our Bezier
curve objects to numpy.poly1d objects and back, lets compute the unit
tangent vector of the above CubicBezier object, b, at t=0.5 in four
different ways.
Tangent vectors (and more on NumPy polynomials)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython2
t = 0.5
### Method 1: the easy way
@ -451,7 +456,7 @@ Description: svgpathtools
Translations (shifts), reversing orientation, and normal vectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
.. code:: ipython2
# Speaking of tangents, let's add a normal vector to the picture
n = b.normal(t)
@ -481,7 +486,7 @@ Description: svgpathtools
Rotations and Translations
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
.. code:: ipython2
# Let's take a Line and an Arc and make some pictures
top_half = Arc(start=-1, radius=1+2j, rotation=0, large_arc=1, sweep=1, end=1)
@ -514,7 +519,7 @@ Description: svgpathtools
``CubicBezier.length()``, and ``Arc.length()`` methods, as well as the
related inverse arc length methods ``.ilength()`` function to do this.
.. code:: python
.. code:: ipython2
# First we'll load the path data from the file test.svg
paths, attributes = svg2paths('test.svg')
@ -556,7 +561,7 @@ Description: svgpathtools
Intersections between Bezier curves
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
.. code:: ipython2
# Let's find all intersections between redpath and the other
redpath = paths[0]
@ -580,7 +585,7 @@ Description: svgpathtools
Here we'll find the `offset
curve <https://en.wikipedia.org/wiki/Parallel_curve>`__ for a few paths.
.. code:: python
.. code:: ipython2
from svgpathtools import parse_path, Line, Path, wsvg
def offset_curve(path, offset_distance, steps=1000):
@ -638,6 +643,7 @@ Description: svgpathtools
This module is under a MIT License.
Keywords: svg,svg path,svg.path,bezier,parse svg path,display svg
Platform: OS Independent
Classifier: Development Status :: 4 - Beta

View File

@ -15,12 +15,10 @@ test.svg
vectorframes.svg
svgpathtools/__init__.py
svgpathtools/bezier.py
svgpathtools/directional_field.py
svgpathtools/misctools.py
svgpathtools/parser.py
svgpathtools/path.py
svgpathtools/paths2svg.py
svgpathtools/pathtools.py
svgpathtools/polytools.py
svgpathtools/smoothing.py
svgpathtools/svg2paths.py
@ -28,10 +26,13 @@ svgpathtools.egg-info/PKG-INFO
svgpathtools.egg-info/SOURCES.txt
svgpathtools.egg-info/dependency_links.txt
svgpathtools.egg-info/top_level.txt
test/groups.svg
test/polygons.svg
test/test.svg
test/test_bezier.py
test/test_generation.py
test/test_parsing.py
test/test_path.py
test/test_pathtools.py
test/test_polytools.py
test/test_svg2paths.py
test/test_svg2paths_groups.py

View File

@ -83,7 +83,7 @@
id="p14"
style="stroke:#ff0000;stroke-width:2."
d="m 1100.,100. 100.,0." />
<g id="concatenated" transform="translate(1150.,150.),rotate(-90.)">
<g id="concatenated" transform="translate(1150.,150.) rotate(-90.)">
<path
id="p15"
style="stroke:#ff0000;stroke-width:2."

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB