Updated with master
commit
30d511577b
|
@ -8,8 +8,8 @@ to make it easy to contribute.
|
||||||
We need better automated testing coverage. Please, submit unittests! See the
|
We need better automated testing coverage. Please, submit unittests! See the
|
||||||
Testing Style section below for info.
|
Testing Style section below for info.
|
||||||
|
|
||||||
Here's a list of things that need (more) unittests
|
Here's a list of things that need (more) unittests:
|
||||||
* OK, well... maybe you could help by filling out this list
|
* TBA (feel free to help)
|
||||||
|
|
||||||
## Submitting Bugs
|
## Submitting Bugs
|
||||||
If you find a bug, please submit an issue along with an **easily reproducible
|
If you find a bug, please submit an issue along with an **easily reproducible
|
||||||
|
|
|
@ -766,7 +766,7 @@
|
||||||
" for distances in offset_distances:\n",
|
" for distances in offset_distances:\n",
|
||||||
" offset_paths.append(offset_curve(path, distances))\n",
|
" offset_paths.append(offset_curve(path, distances))\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Note: This will take a few moments\n",
|
"# Let's take a look\n",
|
||||||
"wsvg(paths + offset_paths, 'g'*len(paths) + 'r'*len(offset_paths), filename='offset_curves.svg')"
|
"wsvg(paths + offset_paths, 'g'*len(paths) + 'r'*len(offset_paths), filename='offset_curves.svg')"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -830,7 +830,7 @@
|
||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython2",
|
"pygments_lexer": "ipython2",
|
||||||
"version": "2.7.13"
|
"version": "2.7.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|
|
@ -585,9 +585,8 @@ curve <https://en.wikipedia.org/wiki/Parallel_curve>`__ for a few paths.
|
||||||
of the 'parallel' offset curve."""
|
of the 'parallel' offset curve."""
|
||||||
nls = []
|
nls = []
|
||||||
for seg in path:
|
for seg in path:
|
||||||
ct = 1
|
|
||||||
for k in range(steps):
|
for k in range(steps):
|
||||||
t = k / steps
|
t = k / float(steps)
|
||||||
offset_vector = offset_distance * seg.normal(t)
|
offset_vector = offset_distance * seg.normal(t)
|
||||||
nl = Line(seg.point(t), seg.point(t) + offset_vector)
|
nl = Line(seg.point(t), seg.point(t) + offset_vector)
|
||||||
nls.append(nl)
|
nls.append(nl)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
4
setup.py
4
setup.py
|
@ -3,7 +3,7 @@ import codecs
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
VERSION = '1.3.2beta'
|
VERSION = '1.3.2'
|
||||||
AUTHOR_NAME = 'Andy Port'
|
AUTHOR_NAME = 'Andy Port'
|
||||||
AUTHOR_EMAIL = 'AndyAPort@gmail.com'
|
AUTHOR_EMAIL = 'AndyAPort@gmail.com'
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ setup(name='svgpathtools',
|
||||||
download_url = 'http://github.com/mathandy/svgpathtools/tarball/'+VERSION,
|
download_url = 'http://github.com/mathandy/svgpathtools/tarball/'+VERSION,
|
||||||
license='MIT',
|
license='MIT',
|
||||||
|
|
||||||
# install_requires=['numpy', 'svgwrite'],
|
install_requires=['numpy', 'svgwrite'],
|
||||||
platforms="OS Independent",
|
platforms="OS Independent",
|
||||||
# test_suite='tests',
|
# test_suite='tests',
|
||||||
requires=['numpy', 'svgwrite'],
|
requires=['numpy', 'svgwrite'],
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
Metadata-Version: 1.1
|
Metadata-Version: 1.1
|
||||||
Name: svgpathtools
|
Name: svgpathtools
|
||||||
Version: 1.3.1
|
Version: 1.3.2
|
||||||
Summary: A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.
|
Summary: A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.
|
||||||
Home-page: https://github.com/mathandy/svgpathtools
|
Home-page: https://github.com/mathandy/svgpathtools
|
||||||
Author: Andy Port
|
Author: Andy Port
|
||||||
Author-email: AndyAPort@gmail.com
|
Author-email: AndyAPort@gmail.com
|
||||||
License: MIT
|
License: MIT
|
||||||
Download-URL: http://github.com/mathandy/svgpathtools/tarball/1.3.1
|
Download-URL: http://github.com/mathandy/svgpathtools/tarball/1.3.2
|
||||||
Description: svgpathtools
|
Description-Content-Type: UNKNOWN
|
||||||
|
Description:
|
||||||
|
svgpathtools
|
||||||
============
|
============
|
||||||
|
|
||||||
svgpathtools is a collection of tools for manipulating and analyzing SVG
|
svgpathtools is a collection of tools for manipulating and analyzing SVG
|
||||||
|
@ -46,11 +48,6 @@ Description: svgpathtools
|
||||||
- compute **inverse arc length**
|
- compute **inverse arc length**
|
||||||
- convert RGB color tuples to hexadecimal color strings and back
|
- 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
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -94,8 +91,6 @@ Description: svgpathtools
|
||||||
module <https://github.com/regebro/svg.path>`__. Interested svg.path
|
module <https://github.com/regebro/svg.path>`__. Interested svg.path
|
||||||
users should see the compatibility notes at bottom of this readme.
|
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
|
Basic Usage
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -126,11 +121,11 @@ Description: svgpathtools
|
||||||
on discontinuous Path objects. A simple workaround is provided, however,
|
on discontinuous Path objects. A simple workaround is provided, however,
|
||||||
by the ``Path.continuous_subpaths()`` method. `↩ <#a1>`__
|
by the ``Path.continuous_subpaths()`` method. `↩ <#a1>`__
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
from __future__ import division, print_function
|
from __future__ import division, print_function
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Coordinates are given as points in the complex plane
|
# Coordinates are given as points in the complex plane
|
||||||
from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc
|
from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc
|
||||||
|
@ -167,7 +162,7 @@ Description: svgpathtools
|
||||||
list. So segments can **append**\ ed, **insert**\ ed, set by index,
|
list. So segments can **append**\ ed, **insert**\ ed, set by index,
|
||||||
**del**\ eted, **enumerate**\ d, **slice**\ d out, etc.
|
**del**\ eted, **enumerate**\ d, **slice**\ d out, etc.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Let's append another to the end of it
|
# Let's append another to the end of it
|
||||||
path.append(CubicBezier(250+350j, 275+350j, 250+225j, 200+100j))
|
path.append(CubicBezier(250+350j, 275+350j, 250+225j, 200+100j))
|
||||||
|
@ -234,7 +229,7 @@ Description: svgpathtools
|
||||||
| Note: Line, Polyline, Polygon, and Path SVG elements can all be
|
| Note: Line, Polyline, Polygon, and Path SVG elements can all be
|
||||||
converted to Path objects using this function.
|
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
|
# Read SVG into a list of path objects and list of dictionaries of attributes
|
||||||
from svgpathtools import svg2paths, wsvg
|
from svgpathtools import svg2paths, wsvg
|
||||||
|
@ -271,7 +266,7 @@ Description: svgpathtools
|
||||||
automatically attempt to open the created svg file in your default SVG
|
automatically attempt to open the created svg file in your default SVG
|
||||||
viewer.
|
viewer.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Let's make a new SVG that's identical to the first
|
# Let's make a new SVG that's identical to the first
|
||||||
wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename='output1.svg')
|
wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename='output1.svg')
|
||||||
|
@ -303,7 +298,7 @@ Description: svgpathtools
|
||||||
that ``path.point(T)=path[k].point(t)``.
|
that ``path.point(T)=path[k].point(t)``.
|
||||||
| There is also a ``Path.t2T()`` method to solve the inverse problem.
|
| There is also a ``Path.t2T()`` method to solve the inverse problem.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
|
|
||||||
|
@ -333,11 +328,11 @@ Description: svgpathtools
|
||||||
True
|
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,
|
| Another great way to work with the parameterizations for ``Line``,
|
||||||
QuadraticBezier, and CubicBezier objects is to convert them to
|
``QuadraticBezier``, and ``CubicBezier`` objects is to convert them to
|
||||||
``numpy.poly1d`` objects. This is done easily using the
|
``numpy.poly1d`` objects. This is done easily using the
|
||||||
``Line.poly()``, ``QuadraticBezier.poly()`` and ``CubicBezier.poly()``
|
``Line.poly()``, ``QuadraticBezier.poly()`` and ``CubicBezier.poly()``
|
||||||
methods.
|
methods.
|
||||||
|
@ -369,9 +364,10 @@ Description: svgpathtools
|
||||||
\end{bmatrix}
|
\end{bmatrix}
|
||||||
\begin{bmatrix}P_0\\P_1\\P_2\\P_3\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:
|
# Example:
|
||||||
b = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j)
|
b = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j)
|
||||||
|
@ -401,15 +397,25 @@ Description: svgpathtools
|
||||||
(-400 + -100j) t + (900 + 300j) t - 600 t + (300 + 100j)
|
(-400 + -100j) t + (900 + 300j) t - 600 t + (300 + 100j)
|
||||||
|
|
||||||
|
|
||||||
To illustrate the awesomeness of being able to convert our Bezier curve
|
The ability to convert between Bezier objects to NumPy polynomial
|
||||||
objects to numpy.poly1d objects and back, lets compute the unit tangent
|
objects is very useful. For starters, we can take turn a list of Bézier
|
||||||
vector of the above CubicBezier object, b, at t=0.5 in four different
|
segments into a NumPy array
|
||||||
ways.
|
|
||||||
|
|
||||||
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
|
t = 0.5
|
||||||
### Method 1: the easy way
|
### Method 1: the easy way
|
||||||
|
@ -451,7 +457,7 @@ Description: svgpathtools
|
||||||
Translations (shifts), reversing orientation, and normal vectors
|
Translations (shifts), reversing orientation, and normal vectors
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Speaking of tangents, let's add a normal vector to the picture
|
# Speaking of tangents, let's add a normal vector to the picture
|
||||||
n = b.normal(t)
|
n = b.normal(t)
|
||||||
|
@ -481,7 +487,7 @@ Description: svgpathtools
|
||||||
Rotations and Translations
|
Rotations and Translations
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Let's take a Line and an Arc and make some pictures
|
# 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)
|
top_half = Arc(start=-1, radius=1+2j, rotation=0, large_arc=1, sweep=1, end=1)
|
||||||
|
@ -514,7 +520,7 @@ Description: svgpathtools
|
||||||
``CubicBezier.length()``, and ``Arc.length()`` methods, as well as the
|
``CubicBezier.length()``, and ``Arc.length()`` methods, as well as the
|
||||||
related inverse arc length methods ``.ilength()`` function to do this.
|
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
|
# First we'll load the path data from the file test.svg
|
||||||
paths, attributes = svg2paths('test.svg')
|
paths, attributes = svg2paths('test.svg')
|
||||||
|
@ -556,7 +562,7 @@ Description: svgpathtools
|
||||||
Intersections between Bezier curves
|
Intersections between Bezier curves
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
# Let's find all intersections between redpath and the other
|
# Let's find all intersections between redpath and the other
|
||||||
redpath = paths[0]
|
redpath = paths[0]
|
||||||
|
@ -580,7 +586,7 @@ Description: svgpathtools
|
||||||
Here we'll find the `offset
|
Here we'll find the `offset
|
||||||
curve <https://en.wikipedia.org/wiki/Parallel_curve>`__ for a few paths.
|
curve <https://en.wikipedia.org/wiki/Parallel_curve>`__ for a few paths.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: ipython2
|
||||||
|
|
||||||
from svgpathtools import parse_path, Line, Path, wsvg
|
from svgpathtools import parse_path, Line, Path, wsvg
|
||||||
def offset_curve(path, offset_distance, steps=1000):
|
def offset_curve(path, offset_distance, steps=1000):
|
||||||
|
@ -638,6 +644,7 @@ Description: svgpathtools
|
||||||
|
|
||||||
This module is under a MIT License.
|
This module is under a MIT License.
|
||||||
|
|
||||||
|
|
||||||
Keywords: svg,svg path,svg.path,bezier,parse svg path,display svg
|
Keywords: svg,svg path,svg.path,bezier,parse svg path,display svg
|
||||||
Platform: OS Independent
|
Platform: OS Independent
|
||||||
Classifier: Development Status :: 4 - Beta
|
Classifier: Development Status :: 4 - Beta
|
||||||
|
|
|
@ -15,23 +15,26 @@ test.svg
|
||||||
vectorframes.svg
|
vectorframes.svg
|
||||||
svgpathtools/__init__.py
|
svgpathtools/__init__.py
|
||||||
svgpathtools/bezier.py
|
svgpathtools/bezier.py
|
||||||
svgpathtools/directional_field.py
|
|
||||||
svgpathtools/misctools.py
|
svgpathtools/misctools.py
|
||||||
svgpathtools/parser.py
|
svgpathtools/parser.py
|
||||||
svgpathtools/path.py
|
svgpathtools/path.py
|
||||||
svgpathtools/paths2svg.py
|
svgpathtools/paths2svg.py
|
||||||
svgpathtools/pathtools.py
|
|
||||||
svgpathtools/polytools.py
|
svgpathtools/polytools.py
|
||||||
svgpathtools/smoothing.py
|
svgpathtools/smoothing.py
|
||||||
svgpathtools/svg2paths.py
|
svgpathtools/svg2paths.py
|
||||||
svgpathtools.egg-info/PKG-INFO
|
svgpathtools.egg-info/PKG-INFO
|
||||||
svgpathtools.egg-info/SOURCES.txt
|
svgpathtools.egg-info/SOURCES.txt
|
||||||
svgpathtools.egg-info/dependency_links.txt
|
svgpathtools.egg-info/dependency_links.txt
|
||||||
|
svgpathtools.egg-info/requires.txt
|
||||||
svgpathtools.egg-info/top_level.txt
|
svgpathtools.egg-info/top_level.txt
|
||||||
|
test/circle.svg
|
||||||
|
test/ellipse.svg
|
||||||
|
test/polygons.svg
|
||||||
|
test/rects.svg
|
||||||
test/test.svg
|
test/test.svg
|
||||||
test/test_bezier.py
|
test/test_bezier.py
|
||||||
test/test_generation.py
|
test/test_generation.py
|
||||||
test/test_parsing.py
|
test/test_parsing.py
|
||||||
test/test_path.py
|
test/test_path.py
|
||||||
test/test_pathtools.py
|
|
||||||
test/test_polytools.py
|
test/test_polytools.py
|
||||||
|
test/test_svg2paths.py
|
|
@ -0,0 +1,2 @@
|
||||||
|
numpy
|
||||||
|
svgwrite
|
|
@ -572,7 +572,7 @@ class Line(object):
|
||||||
d = (other_seg.start.imag, other_seg.end.imag)
|
d = (other_seg.start.imag, other_seg.end.imag)
|
||||||
denom = ((a[1] - a[0])*(d[0] - d[1]) -
|
denom = ((a[1] - a[0])*(d[0] - d[1]) -
|
||||||
(b[1] - b[0])*(c[0] - c[1]))
|
(b[1] - b[0])*(c[0] - c[1]))
|
||||||
if denom == 0:
|
if np.isclose(denom, 0):
|
||||||
return []
|
return []
|
||||||
t1 = (c[0]*(b[0] - d[1]) -
|
t1 = (c[0]*(b[0] - d[1]) -
|
||||||
c[1]*(b[0] - d[0]) -
|
c[1]*(b[0] - d[0]) -
|
||||||
|
@ -1300,10 +1300,13 @@ class Arc(object):
|
||||||
# delta is the angular distance of the arc (w.r.t the circle)
|
# delta is the angular distance of the arc (w.r.t the circle)
|
||||||
# theta is the angle between the positive x'-axis and the start point
|
# theta is the angle between the positive x'-axis and the start point
|
||||||
# on the circle
|
# on the circle
|
||||||
|
u1_real_rounded = u1.real
|
||||||
|
if u1.real > 1 or u1.real < -1:
|
||||||
|
u1_real_rounded = round(u1.real)
|
||||||
if u1.imag > 0:
|
if u1.imag > 0:
|
||||||
self.theta = degrees(acos(u1.real))
|
self.theta = degrees(acos(u1_real_rounded))
|
||||||
elif u1.imag < 0:
|
elif u1.imag < 0:
|
||||||
self.theta = -degrees(acos(u1.real))
|
self.theta = -degrees(acos(u1_real_rounded))
|
||||||
else:
|
else:
|
||||||
if u1.real > 0: # start is on pos u_x axis
|
if u1.real > 0: # start is on pos u_x axis
|
||||||
self.theta = 0
|
self.theta = 0
|
||||||
|
@ -2060,7 +2063,7 @@ class Path(MutableSequence):
|
||||||
if np.isclose(t, 0) and (seg_idx != 0 or self.end==self.start):
|
if np.isclose(t, 0) and (seg_idx != 0 or self.end==self.start):
|
||||||
previous_seg_in_path = self._segments[
|
previous_seg_in_path = self._segments[
|
||||||
(seg_idx - 1) % len(self._segments)]
|
(seg_idx - 1) % len(self._segments)]
|
||||||
if not seg.joins_smoothl_with(previous_seg_in_path):
|
if not seg.joins_smoothly_with(previous_seg_in_path):
|
||||||
return float('inf')
|
return float('inf')
|
||||||
elif np.isclose(t, 1) and (seg_idx != len(self) - 1 or self.end==self.start):
|
elif np.isclose(t, 1) and (seg_idx != len(self) - 1 or self.end==self.start):
|
||||||
next_seg_in_path = self._segments[
|
next_seg_in_path = self._segments[
|
||||||
|
|
|
@ -88,7 +88,7 @@ def disvg(paths=None, colors=None,
|
||||||
openinbrowser=True, timestamp=False,
|
openinbrowser=True, timestamp=False,
|
||||||
margin_size=0.1, mindim=600, dimensions=None,
|
margin_size=0.1, mindim=600, dimensions=None,
|
||||||
viewbox=None, text=None, text_path=None, font_size=None,
|
viewbox=None, text=None, text_path=None, font_size=None,
|
||||||
attributes=None, svg_attributes=None):
|
attributes=None, svg_attributes=None, svgwrite_debug=False):
|
||||||
"""Takes in a list of paths and creates an SVG file containing said paths.
|
"""Takes in a list of paths and creates an SVG file containing said paths.
|
||||||
REQUIRED INPUTS:
|
REQUIRED INPUTS:
|
||||||
:param paths - a list of paths
|
:param paths - a list of paths
|
||||||
|
@ -152,14 +152,22 @@ def disvg(paths=None, colors=None,
|
||||||
paths. Note: This will override any other conflicting settings.
|
paths. Note: This will override any other conflicting settings.
|
||||||
|
|
||||||
:param svg_attributes - a dictionary of attributes for output svg.
|
:param svg_attributes - a dictionary of attributes for output svg.
|
||||||
Note 1: This will override any other conflicting settings.
|
|
||||||
Note 2: Setting `svg_attributes={'debug': False}` may result in a
|
:param svgwrite_debug - This parameter turns on/off `svgwrite`'s
|
||||||
significant increase in speed.
|
debugging mode. By default svgwrite_debug=False. This increases
|
||||||
|
speed and also prevents `svgwrite` from raising of an error when not
|
||||||
|
all `svg_attributes` key-value pairs are understood.
|
||||||
|
|
||||||
NOTES:
|
NOTES:
|
||||||
-The unit of length here is assumed to be pixels in all variables.
|
* The `svg_attributes` parameter will override any other conflicting
|
||||||
|
settings.
|
||||||
|
|
||||||
-If this function is used multiple times in quick succession to
|
* Any `extra` parameters that `svgwrite.Drawing()` accepts can be
|
||||||
|
controlled by passing them in through `svg_attributes`.
|
||||||
|
|
||||||
|
* The unit of length here is assumed to be pixels in all variables.
|
||||||
|
|
||||||
|
* If this function is used multiple times in quick succession to
|
||||||
display multiple SVGs (all using the default filename), the
|
display multiple SVGs (all using the default filename), the
|
||||||
svgviewer/browser will likely fail to load some of the SVGs in time.
|
svgviewer/browser will likely fail to load some of the SVGs in time.
|
||||||
To fix this, use the timestamp attribute, or give the files unique
|
To fix this, use the timestamp attribute, or give the files unique
|
||||||
|
@ -277,12 +285,15 @@ def disvg(paths=None, colors=None,
|
||||||
szy = str(mindim) + 'px'
|
szy = str(mindim) + 'px'
|
||||||
|
|
||||||
# Create an SVG file
|
# Create an SVG file
|
||||||
if svg_attributes:
|
if svg_attributes is not None:
|
||||||
szx = svg_attributes.get("width", szx)
|
szx = svg_attributes.get("width", szx)
|
||||||
szy = svg_attributes.get("height", szy)
|
szy = svg_attributes.get("height", szy)
|
||||||
dwg = Drawing(filename=filename, size=(szx, szy), **svg_attributes)
|
debug = svg_attributes.get("debug", svgwrite_debug)
|
||||||
|
dwg = Drawing(filename=filename, size=(szx, szy), debug=debug,
|
||||||
|
**svg_attributes)
|
||||||
else:
|
else:
|
||||||
dwg = Drawing(filename=filename, size=(szx, szy), viewBox=viewbox)
|
dwg = Drawing(filename=filename, size=(szx, szy), debug=svgwrite_debug,
|
||||||
|
viewBox=viewbox)
|
||||||
|
|
||||||
# add paths
|
# add paths
|
||||||
if paths:
|
if paths:
|
||||||
|
@ -377,7 +388,7 @@ def wsvg(paths=None, colors=None,
|
||||||
openinbrowser=False, timestamp=False,
|
openinbrowser=False, timestamp=False,
|
||||||
margin_size=0.1, mindim=600, dimensions=None,
|
margin_size=0.1, mindim=600, dimensions=None,
|
||||||
viewbox=None, text=None, text_path=None, font_size=None,
|
viewbox=None, text=None, text_path=None, font_size=None,
|
||||||
attributes=None, svg_attributes=None):
|
attributes=None, svg_attributes=None, svgwrite_debug=False):
|
||||||
"""Convenience function; identical to disvg() except that
|
"""Convenience function; identical to disvg() except that
|
||||||
openinbrowser=False by default. See disvg() docstring for more info."""
|
openinbrowser=False by default. See disvg() docstring for more info."""
|
||||||
disvg(paths, colors=colors, filename=filename,
|
disvg(paths, colors=colors, filename=filename,
|
||||||
|
@ -386,4 +397,5 @@ def wsvg(paths=None, colors=None,
|
||||||
openinbrowser=openinbrowser, timestamp=timestamp,
|
openinbrowser=openinbrowser, timestamp=timestamp,
|
||||||
margin_size=margin_size, mindim=mindim, dimensions=dimensions,
|
margin_size=margin_size, mindim=mindim, dimensions=dimensions,
|
||||||
viewbox=viewbox, text=text, text_path=text_path, font_size=font_size,
|
viewbox=viewbox, text=text, text_path=text_path, font_size=font_size,
|
||||||
attributes=attributes, svg_attributes=svg_attributes)
|
attributes=attributes, svg_attributes=svg_attributes,
|
||||||
|
svgwrite_debug=svgwrite_debug)
|
||||||
|
|
|
@ -10,6 +10,13 @@ import xml.etree.cElementTree as etree
|
||||||
from .parser import parse_path
|
from .parser import parse_path
|
||||||
|
|
||||||
|
|
||||||
|
COORD_PAIR_TMPLT = re.compile(
|
||||||
|
r'([\+-]?\d*[\.\d]\d*[eE][\+-]?\d+|[\+-]?\d*[\.\d]\d*)' +
|
||||||
|
r'(?:\s*,\s*|\s+|(?=-))' +
|
||||||
|
r'([\+-]?\d*[\.\d]\d*[eE][\+-]?\d+|[\+-]?\d*[\.\d]\d*)'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def ellipse2pathd(ellipse):
|
def ellipse2pathd(ellipse):
|
||||||
"""converts the parameters from an ellipse or a circle to a string
|
"""converts the parameters from an ellipse or a circle to a string
|
||||||
for a Path object d-attribute"""
|
for a Path object d-attribute"""
|
||||||
|
@ -37,23 +44,21 @@ def ellipse2pathd(ellipse):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def polyline2pathd(polyline_d):
|
def polyline2pathd(polyline_d, is_polygon=False):
|
||||||
"""converts the string from a polyline points-attribute to a string
|
"""converts the string from a polyline points-attribute to a string for a
|
||||||
for a Path object d-attribute"""
|
Path object d-attribute"""
|
||||||
try:
|
points = COORD_PAIR_TMPLT.findall(polyline_d)
|
||||||
points = polyline_d['points']
|
closed = (float(points[0][0]) == float(points[-1][0]) and
|
||||||
except:
|
float(points[0][1]) == float(points[-1][1]))
|
||||||
pass
|
|
||||||
points = polyline_d.replace(', ', ',')
|
|
||||||
points = points.replace(' ,', ',')
|
|
||||||
points = points.split()
|
|
||||||
|
|
||||||
closed = points[0] == points[-1]
|
# 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 is_polygon and closed:
|
||||||
|
points.append(points[0])
|
||||||
|
|
||||||
d = 'M' + points.pop(0).replace(',', ' ')
|
d = 'M' + 'L'.join('{0} {1}'.format(x,y) for x,y in points)
|
||||||
for p in points:
|
if is_polygon or closed:
|
||||||
d += 'L' + p.replace(',', ' ')
|
|
||||||
if closed:
|
|
||||||
d += 'z'
|
d += 'z'
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@ -62,31 +67,8 @@ def polygon2pathd(polyline_d):
|
||||||
"""converts the string from a polygon points-attribute to a string
|
"""converts the string from a polygon points-attribute to a string
|
||||||
for a Path object d-attribute.
|
for a Path object d-attribute.
|
||||||
Note: For a polygon made from n points, the resulting path will be
|
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).
|
composed of n lines (even if some of these lines have length zero)."""
|
||||||
"""
|
return polyline2pathd(polyline_d, True)
|
||||||
try:
|
|
||||||
points = polyline_d['points']
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
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 rect2pathd(rect):
|
def rect2pathd(rect):
|
||||||
|
@ -104,9 +86,11 @@ def rect2pathd(rect):
|
||||||
"".format(x0, y0, x1, y1, x2, y2, x3, y3))
|
"".format(x0, y0, x1, y1, x2, y2, x3, y3))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def line2pathd(l):
|
def line2pathd(l):
|
||||||
return 'M' + l['x1'] + ' ' + l['y1'] + 'L' + l['x2'] + ' ' + l['y2']
|
return 'M' + l['x1'] + ' ' + l['y1'] + 'L' + l['x2'] + ' ' + l['y2']
|
||||||
|
|
||||||
|
|
||||||
CONVERSIONS = {'circle': ellipse2pathd,
|
CONVERSIONS = {'circle': ellipse2pathd,
|
||||||
'ellipse': ellipse2pathd,
|
'ellipse': ellipse2pathd,
|
||||||
'line': line2pathd,
|
'line': line2pathd,
|
||||||
|
@ -114,6 +98,7 @@ CONVERSIONS = {'circle': ellipse2pathd,
|
||||||
'polygon': polygon2pathd,
|
'polygon': polygon2pathd,
|
||||||
'rect': rect2pathd}
|
'rect': rect2pathd}
|
||||||
|
|
||||||
|
|
||||||
def svg2paths(svg_file_location, return_svg_attributes=False,
|
def svg2paths(svg_file_location, return_svg_attributes=False,
|
||||||
conversions=CONVERSIONS, return_tree=False):
|
conversions=CONVERSIONS, return_tree=False):
|
||||||
"""Converts SVG to list of Path objects and attribute dictionaries.
|
"""Converts SVG to list of Path objects and attribute dictionaries.
|
||||||
|
@ -203,5 +188,3 @@ def svg2paths3(svg_file_location, return_svg_attributes=True,
|
||||||
return svg2paths(svg_file_location=svg_file_location,
|
return svg2paths(svg_file_location=svg_file_location,
|
||||||
return_svg_attributes=return_svg_attributes,
|
return_svg_attributes=return_svg_attributes,
|
||||||
conversions=conversions, return_tree=return_tree)
|
conversions=conversions, return_tree=return_tree)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" ?>
|
<?xml version="1.0" ?>
|
||||||
<svg baseProfile="full" height="600px" version="1.1" viewBox="-10.05 -10.05 120.1 120.1" width="600px" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg baseProfile="full" height="600px" version="1.1" viewBox="-10.05 -10.05 120.1 120.1" width="600px" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<polygon points="55.5,0 55.5, 50 105.5,50" style="stroke:purple;stroke-width:1"/>
|
<polygon points="55.5,0 55.5, 50 105.5,50" style="stroke:purple;stroke-width:1"/>
|
||||||
<polygon points="0,0 0,100 100,100 0,0" style="stroke:purple;stroke-width:1"/>
|
<polygon points="0,0.0 0,-100 , 1.0e-1-1e2,0,0" style="stroke:purple;stroke-width:1"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 422 B After Width: | Height: | Size: 431 B |
|
@ -353,6 +353,18 @@ class QuadraticBezierTest(unittest.TestCase):
|
||||||
|
|
||||||
class ArcTest(unittest.TestCase):
|
class ArcTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_trusting_acos(self):
|
||||||
|
"""`u1.real` is > 1 in this arc due to numerical error."""
|
||||||
|
try:
|
||||||
|
a1 = Arc(start=(160.197+102.925j),
|
||||||
|
radius=(0.025+0.025j),
|
||||||
|
rotation=0.0,
|
||||||
|
large_arc=False,
|
||||||
|
sweep=True,
|
||||||
|
end=(160.172+102.95j))
|
||||||
|
except ValueError:
|
||||||
|
self.fail("Arc() raised ValueError unexpectedly!")
|
||||||
|
|
||||||
def test_points(self):
|
def test_points(self):
|
||||||
arc1 = Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j)
|
arc1 = Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j)
|
||||||
self.assertAlmostEqual(arc1.center, 100 + 0j)
|
self.assertAlmostEqual(arc1.center, 100 + 0j)
|
||||||
|
@ -979,6 +991,19 @@ class Test_intersect(unittest.TestCase):
|
||||||
self.assertTrue(len(yix), 1)
|
self.assertTrue(len(yix), 1)
|
||||||
###################################################################
|
###################################################################
|
||||||
|
|
||||||
|
def test_line_line_0(self):
|
||||||
|
l0 = Line(start=(25.389999999999997+99.989999999999995j), end=(25.389999999999997+90.484999999999999j))
|
||||||
|
l1 = Line(start=(25.390000000000001+84.114999999999995j), end=(25.389999999999997+74.604202137430320j))
|
||||||
|
i = l0.intersect(l1)
|
||||||
|
assert(len(i)) == 0
|
||||||
|
|
||||||
|
def test_line_line_1(self):
|
||||||
|
l0 = Line(start=(-124.705378549+327.696674827j), end=(12.4926214511+121.261674827j))
|
||||||
|
l1 = Line(start=(-12.4926214511+121.261674827j), end=(124.705378549+327.696674827j))
|
||||||
|
i = l0.intersect(l1)
|
||||||
|
assert(len(i)) == 1
|
||||||
|
assert(abs(l0.point(i[0][0])-l1.point(i[0][1])) < 1e-9)
|
||||||
|
|
||||||
|
|
||||||
class TestPathTools(unittest.TestCase):
|
class TestPathTools(unittest.TestCase):
|
||||||
# moved from test_pathtools.py
|
# moved from test_pathtools.py
|
||||||
|
|
|
@ -20,9 +20,9 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
|
|
||||||
# triangular quadrilateral (with a redundant 4th "closure" point)
|
# triangular quadrilateral (with a redundant 4th "closure" point)
|
||||||
path = paths[1]
|
path = paths[1]
|
||||||
path_correct = Path(Line(0+0j, 0+100j),
|
path_correct = Path(Line(0+0j, 0-100j),
|
||||||
Line(0+100j, 100+100j),
|
Line(0-100j, 0.1-100j),
|
||||||
Line(100+100j, 0+0j),
|
Line(0.1-100j, 0+0j),
|
||||||
Line(0+0j, 0+0j) # result of redundant point
|
Line(0+0j, 0+0j) # result of redundant point
|
||||||
)
|
)
|
||||||
self.assertTrue(path.isclosed())
|
self.assertTrue(path.isclosed())
|
||||||
|
|
Loading…
Reference in New Issue