Giter Site home page Giter Site logo

svgpathtools's People

Contributors

abey79 avatar andersgb avatar canule avatar catherineh avatar chanicpanic avatar davidromeroantequera avatar dervedro avatar elenzil avatar flyingsamson avatar jpcofr avatar kasbah avatar mathandy avatar mdejean avatar msea1 avatar mxgrey avatar nataliats avatar njhurst avatar remi-pr avatar saraedum avatar sebkuzminsky avatar skef avatar sumeet- avatar taoari avatar tatarize avatar ugultopu avatar vrroom avatar wesbz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

svgpathtools's Issues

Bug: Path curvature not correctly scaled

I was a bit puzzled by a discrepancy in the curvature of a complete path and the curvature of its corresponding segment. Try running the following

from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc
seg1 = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j)  # A cubic beginning at (300, 100) and ending at (200, 300)
seg2 = Line(200+300j, 250+350j)  # A line beginning at (200, 300) and ending at (250, 350)
path = Path(seg1, seg2)  # A path traversing the cubic and then the line
T=0.5
k = path.curvature(T)
idx,t = path.T2t(T)
k_seg = path[idx].curvature(t)
print(k,k_seg)

This bug arises because the derivatives of the Path object are taken with respect to the t variable of the segment and not the T variable of the Path object itself. This can be fixed by modifying the following lines.

dz = self.derivative(t)

ddz = self.derivative(t, n=2)

It would also seem to be possible to use
return seg.curvature(t)
rather than the explicit formula itself.

A function like disvg but returning a svgwrite.Drawing would be helpful.

It would be helpful if your great lib would offer a function which would not write to a .svg file, like the existing disvg(), but returning the svgwrite.Drawing object.
This way it would be possible to add additional stuff like defs to the Drawing. Or is there already a way to do this?

Rect not closed as output

Hi,

Thanks for your good library !

An SVG file contain a simple rect. If i open this SVG file with a rect (=closed) with :
paths,attribute=spt.svg2paths('test.svg')

I obtain :
paths
Out[14]:
[Path(Line(start=(28.571428299+29.5050621033j), end=(325.714281082+29.5050621033j)),
Line(start=(325.714281082+29.5050621033j), end=(325.714281082+280.933635711j)),
Line(start=(325.714281082+280.933635711j), end=(28.571428299+280.933635711j)),
Line(start=(28.571428299+280.933635711j), end=(28.571428299+29.5050621033j)))]

And now, if I display the d string, I obtain :
paths[0].d()
Out[15]: 'M 28.571428299,29.5050621033 L 325.714281082,29.5050621033 L 325.714281082,280.933635711 L 28.571428299,280.933635711 L 28.571428299,29.5050621033'

And internally :
paths[0].isclosed()
Out[11]: True

But when I open the file with :
spt.disvg(paths)

It's logically open (see d), not closed. I think the d attribute should be :
'M 28.571428299,29.5050621033 L 325.714281082,29.5050621033 L 325.714281082,280.933635711 L 28.571428299,280.933635711 Z'

The problem seem exist with circle so.

I think Z should be added to d field when path is closest : isn't it ?

Xavier HINAULT
France

useSandT?

I want output a d string using 'S' instead of 'C'. the d() methode has the useSandT argument to influence this, but I haven't found anyway to set this when using disvg().

The Dom parser crashes on Polyline and Polygon

The Document class uses a dict to lookup the functions for the leaf node objects and calls them from the svg_to_paths file but the polyline and polygon functions do not properly take a group they specifically take a points_string, so they actually fail when called like that.

"""converts the string from a polyline points-attribute to a string for a
Path object d-attribute"""

Compare this to something like ellipse:

"""converts the parameters from an ellipse or a circle to a string for a 
Path object d-attribute"""

Do note, that simply fixing the polyline function to set polyline_d = polyline['d'] will seemingly change the functionality of dom2dict() in svg_to_paths:

    if convert_polygons_to_paths:
        pgons = [dom2dict(el) for el in doc.getElementsByTagName('polygon')]
        d_strings += [polygon2pathd(pg['points']) for pg in pgons]
        attribute_dictionary_list += pgons

I rewrote the functions in a consistent manner in the svg_parser.py file in pull request #69. Generally because it the inconsistency of extracting the points early for those two conversions bits was an eyesore.

zero lenght closing lines after path parsing

There is a issue with closed paths. If the penultimate command ends on the start point, the z command creates a zero length svgpathtools.path.Line. That breaks a lot of things later, something like div by zero. I think a simple check for current_pos == start_pos in parser will do well.

Path drawing position bugs

I am drawing contours, referenced from origin (0,0). Those objects have strictly positive coordinates.
However, when drawing them with wsvg, the following issues appear:

  • Objects are oriented pointing downwards, with negative coordinates
  • Objects are referenced from document origin (located at (0, document_height)), not global origin.

Is there a way to avoid this behavior that sound like a bug to me ?

EDIT:
From my experiments, it looks like document coordinate system is oriented y-down, while SVG coordinate system is y-up. Is it normal ?

Text bounding box

Could you suggest a method to extract text and their bounding boxe coordinates with respect to viewbox .

SVG Groups are ignored. Especially transformations acting on the paths inside the group.

Example in an SVG created in Inkscape:

<g
id="g3450-3"
transform="matrix(-1e-4,-1,1,-1e-4,670.77002,132.92999)">
<path
inkscape:connector-curvature="0"
d="m 28.35,0 c 0,15.65621 -12.69107,28.3485 -28.34728,28.35"
style="fill:none;stroke:#000000;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none"
id="path3452-8" />

The group transformation is ignored when calling svgpathtools.svg2paths().

Functions for converting other SVG primitives to paths not importable

We need the functionality used in the primitive conversion functions: ellipse2pathd, polyline2pathd, polygon2pathd and rect2pathd

Unfortunately, as svgpathtools.__init__ exposes a function with the same name as the module svg2paths, importing these externally is not possible.

Would it be possible to allow these to be importable? I am happy to do any refactoring required if this is a change you would accept. At the moment we have the code copy / pasted in our repo, and would be nicer to avoid this duplication.

Bezier curve inflection points

Hi,

First of all, thank you for the svgpathtools.
It has saved me lots of time.

Really just a small issue.
I did not see a convenient way to obtain the inflection points of a bezier curve.
@mathandy, Would you accept a contribution for that?

All the best,
olaf

Writing long paths to file takes a while

Saving long paths with a lot of segments is really slow. I figured out svgwrite runs by default with some debug flags. Svgwrite does some validation stuff like checking syntax of path's d attribute. The parser that they use runs amok.

I think the best approach is to disable debug mode and to turn validation off (#7). The data you put into svgwrite is valid, paths are syntactically correct. And if some arguments are broken, most programs can deal with it.

Approximate cubic Bézie using line segments

Hi folks - thanks for the great library.

I'm not sure if this functionality is within the scope of this library, but I'm going to put it up for discussion here anyway.

I'd like to approximate a CubicBezier with a series of Line objects. This has many applications, for example in drawing SVGs using an XY plotter (which is my specific need).

The trivial way to do this is to divide the curve into an equal number of points and create Line objects connecting each new segment along the path. This however is not guaranteed to give a good representation of the curve, and is likely to contain an excessive number of points.

A good way to make this approximation is to do it in an adaptive way. Break the curve into smaller curves (CubicBezier.split()) until a certain flatness criterion is met. After doing a bit of research, this seems to be the most referneced article on the subject. The method is critiqued here, especially for not having consulted prior academic research on the subject.

I'd like to implement something like this, and possibly contribute it to this library if you see that it would fit. But before I do so, I'd like to get your recommendations on what algorithm would be appropriate for this purpose.

The antigrain algorithm is one possibility, and in the google groups thread there's another interesting suggestion:

A well-known flatness test is both cheaper and more reliable than the ones you have tried. The essential observation is that when the curve is a uniform speed straight line from end to end, the control points are evenly spaced from beginning to end. Therefore, our measure of how far we deviate from that ideal uses distance of the middle controls, not from the line itself, but from their ideal arrangement. Point 2 should be half-way between points 1 and 3; point 3 should be half-way between points 2 and 4.
This, too, can be improved. Yes, we can eliminate the square roots in the distance tests, retaining the Euclidean metric; but the taxicab metric is faster, and also safe. The length of displacement (x,y) in this metric is |x|+|y|.

What do you suggest as a good algorithm to implement in this case? Would love some feedback.

Converting complex bbox coordinates to ViewBox relative points

Hello,
I have an svg with viewbox(0,0,612,792) . bbox on any path elements gives complex numbers .Is there a way to convert them into coordinates with respect to top left corner of viewbox?
Path(Line(start=(790.996+1827j), end=(790.996+1804j)))
I am using path[0].length() to find lines with certain length and replace them with lines across/along the viewbox at same locations.

If line1= x1,y1,x1,y2 then replacement would be x1,y1,x1,612

Create relatives path instead of absolute.

Is there anyway to get out svg to generate a relative path instead of an absolute one?

Instead of this:
<path d="M 0.0,1.0 L 2.0,1.0 L 3.0,2.0" fill="none" stroke="#000000" stroke-width="1.175"/>

something like this:
<path d="m 18.815667,1 h 2 l 1,1" fill="none" stroke="#000000" stroke-width="1.175"/>

Convert SVG to list of lines and arcs

Hello, i am working on SVG to g-code convertor for bCNC and i need way to subdivide the SVG to list of paths made just from LINEs and ARCs. Because g-code does not support anything else except lines and arcs. Is there way to do this using your library? That would mean to approximate and replace both Bezier types using lines (or arcs).

Possible to save SVG with mm as standard unit?

Question as above.

Context: I am currently working on a lasercutting project. All our toolchains use mm as unit. Currently, when using paths2svg.wsvg, then the unit is set to px. Is there a way to change this to mm by default?

intersection of a line and cubicbezier

I found that there is an issue with intersection function. May be there is no issue but this code did something that I do not expect. So imagine there are 2 Paths and you want find out the intersection point.

from svgpathtools import *

# some shapes
A = Line(start=(0+200j), end=(300+200j))
B = CubicBezier(start=(50+150j), control1=(70+200j), control2=(120+250j), end=(200+300j))

# intersection points ((T1, seg1, t1), (T2, seg2, t2))
A_intersection = Path(A).intersect(B, justonemode=True)
B_intersection = Path(B).intersect(A, justonemode=True)

disvg([A,B], nodes=[A.point(A_intersection[0][0]), B.point(B_intersection[0][0])])

I except 2 nodes with approximately same coordinates, but they differs. May be using fixed indices at A_intersection[0][0] is not the best option to figure out the T value of the path. Let's see what is in A_intersection and B_intersection:

>>> A_intersection
((0.33333333333333331, Line(start=200j, end=(200+200j)), 0.33333333333333331),
 (0.40000000000000002,
  CubicBezier(start=(50+150j), control1=(70+200j), control2=(120+250j), end=(200+300j)),
  0.40000000000000002))
>>> B_intersection
((0.33333333333333331,
  CubicBezier(start=(50+150j), control1=(70+200j), control2=(120+250j), end=(200+300j)),
  0.33333333333333331),
 (0.40000000000000002, Line(start=200j, end=(200+200j)), 0.40000000000000002))

It looks for me like if you search for intersection of Line and CubicBezier and return values of intersect() on Line object have wrong positions. So you get something like ((T2, seg1, t2), (T1, seg2, t1))

When installing with pip3, svg2paths is not matching documentation

First time using svgpathtools today, also not too experienced with python. I basically want to use svgpathtools to run some checks and based on them change the path element slightly. That's just how I got to the issue though:

The documentation specifies the following function arguments:

svg2paths(svg_file_location, return_svg_attributes=False, convert_circles_to_paths=True, convert_ellipses_to_paths=True, convert_lines_to_paths=True, convert_polylines_to_paths=True, convert_polygons_to_paths=True, convert_rectangles_to_paths=True)

I installed svgpathtools with pip3 install svgpathtools. When checking for the function used with inspect.getsource(svg2paths), I get the following:

def svg2paths(svg_file_location,
              convert_lines_to_paths=True,
              convert_polylines_to_paths=True,
              convert_polygons_to_paths=True,
              return_svg_attributes=False):
    """
    Converts an SVG file into a list of Path objects and a list of
    dictionaries containing their attributes.  This currently supports
    SVG Path, Line, Polyline, and Polygon elements.
    :param svg_file_location: the location of the svg file
    :param convert_lines_to_paths: Set to False to disclude SVG-Line objects
    (converted to Paths)
    :param convert_polylines_to_paths: Set to False to disclude SVG-Polyline
    objects (converted to Paths)
    :param convert_polygons_to_paths: Set to False to disclude SVG-Polygon
    objects (converted to Paths)
    :param return_svg_attributes: Set to True and a dictionary of
    svg-attributes will be extracted and returned
    :return: list of Path objects, list of path attribute dictionaries, and
    (optionally) a dictionary of svg-attributes

Where does this decrepancy come from? Just a general question, since until now, I didn't have any problems installing modules with pip3

Bug in Line.intersect(Line)

I found a bug where two obviously non-intersecting Lines still report an intersection using Line.intersect(). The bug is captured in this commit: SebKuzminsky/svgpathtools@847b270b
It is highly sensitive to the precise starting and ending points of the two lines - if you round the values down to three significant places (like Line.__repr__() does) then the bug does not trigger.

Memory leak of CubicBezier.length()

I found that there is a memory leak for function CubicBezier.length().
The test code is below:

    for i in range(10000):
        paths, _, _ = svg2paths2(file)
        seg = paths[1][0]
        if type(seg) is CubicBezier:
            print(seg.length())

My test environment is OSX10.12, python3.6.

SVG Style when using wsvg

Apologies if this is not an issue and the way I'm doing it. I'm writing to an svg file using wsvg but it doesn't retain the style. Instead it seems to default to black and the stroke vanishes. When I add this style back in via illustrator then it works fine. Is there any way of keeping the style from when you read an svg to when you write it?

Path.area() fails if it includes Arc objects

Path.area() calls poly() on each segment, but Arc() doesn't provide that method. The area can be approximated using linear interpolation of the Arc:

def approximate_path_area(path):

    """Approximates the path area by converting each Arc to 1,000
    Lines."""

    assert(path.isclosed())
    tmp = svgpathtools.path.Path()
    for seg in path:
        if type(seg) == svgpathtools.path.Arc:
            for i in range(0, 1000):
                t0 = i/1000.0
                t1 = (i+1)/1000.0
                l = svgpathtools.path.Line(start=seg.point(t0), end=seg.point(t1))
                tmp.append(l)
        else:
            tmp.append(seg)
    return tmp.area()

weird code/dist issue in svg2paths/svg_to_paths

Hi

I'm having a weird issue. i install svgpathtools (v 1.3.3) via a setup.py script in a package i have. For some reason the installed package ends up with two similar files that are not present in the repo here, as far as i can tell.

Here is an example:


15:37:46•anders@xyz:/usr/local/lib/python3.5/dist-packages
  cd svgpathtools-1.3.3-py3.5.egg 

15:37:48•anders@xyz:/usr/local/lib/python3.5/dist-packages/svgpathtools-1.3.3-py3.5.egg
  ls
EGG-INFO  svgpathtools

15:37:48•anders@xyz:/usr/local/lib/python3.5/dist-packages/svgpathtools-1.3.3-py3.5.egg
  cd svgpathtools 
15:37:51•anders@xyz:/usr/local/lib/python3.5/dist-packages/svgpathtools-1.3.3-py3.5.egg/svgpathtools
  ll
total 180K
-rw-r--r-- 1 root staff  14K feb.  25 15:37 bezier.py
-rw-r--r-- 1 root staff  654 feb.  25 15:37 directional_field.py
-rw-r--r-- 1 root staff  890 feb.  25 15:37 __init__.py
-rw-r--r-- 1 root staff 2,0K feb.  25 15:37 misctools.py
-rw-r--r-- 1 root staff 7,0K feb.  25 15:37 parser.py
-rw-r--r-- 1 root staff  92K feb.  25 15:37 path.py
-rw-r--r-- 1 root staff  15K feb.  25 15:37 paths2svg.py
-rw-r--r-- 1 root staff  439 feb.  25 15:37 pathtools.py
-rw-r--r-- 1 root staff 2,5K feb.  25 15:37 polytools.py
-rw-r--r-- 1 root staff 7,5K feb.  25 15:37 smoothing.py
-rw-r--r-- 1 root staff 4,8K feb.  25 15:37 svg2paths.py   <--   NOTICE
-rw-r--r-- 1 root staff 8,3K feb.  25 15:37 svg_to_paths.py  <-- NOTICE

15:37:51•anders@xyz:/usr/local/lib/python3.5/dist-packages/svgpathtools-1.3.3-py3.5.egg/svgpathtools
  ag svg2paths
__init__.py
17:    from .svg2paths import svg2paths, svg2paths2

svg2paths.py
2:The main tool being the svg2paths() function."""
34:def svg2paths(svg_file_location,
114:def svg2paths2(svg_file_location,
119:    """Convenience function; identical to svg2paths() except that
120:    return_svg_attributes=True by default.  See svg2paths() docstring for more
122:    return svg2paths(svg_file_location=svg_file_location,

svg_to_paths.py
2:The main tool being the svg2paths() function."""
92:def svg2paths(svg_file_location,
110:            `svg2paths2()` function.
129:        dict (optional): A dictionary of svg-attributes (see `svg2paths2()`).
193:def svg2paths2(svg_file_location,
201:    """Convenience function; identical to svg2paths() except that
202:    return_svg_attributes=True by default.  See svg2paths() docstring for more
204:    return svg2paths(svg_file_location=svg_file_location,
15:38:18•anders@xyz:/usr/local/lib/python3.5/dist-packages/svgpathtools-1.3.3-py3.5.egg/svgpathtools

I noticed this because one version of svg2paths is capable of converting rectangles (svg rect) and the other is not.

I made sure this package was not installed in this location before i performed the installation.

The wheel that is installed (https://files.pythonhosted.org/packages/71/96/cc91050f3b53c2cea0eda18f371d0584e7f43713ce606738384e8001a877/svgpathtools-1.3.3-py2.py3-none-any.whl#sha256=7f7bdafe2c03b312178460104705e1d554d8cf36c898bec41bdce9fed3504746) does contain both files. From the setuptools output:

Searching for svgpathtools==1.3.3
Reading https://pypi.python.org/simple/svgpathtools/
Downloading https://files.pythonhosted.org/packages/71/96/cc91050f3b53c2cea0eda18f371d0584e7f43713ce606738384e8001a877/svgpathtools-1.3.3-py2.py3-none-any.whl#sha256=7f7bdafe2c03b312178460104705e1d554d8cf36c898bec41bdce9fed3504746
Best match: svgpathtools 1.3.3
Processing svgpathtools-1.3.3-py2.py3-none-any.whl
Installing svgpathtools-1.3.3-py2.py3-none-any.whl to /usr/local/lib/python3.5/dist-packages
writing requirements to /usr/local/lib/python3.5/dist-packages/svgpathtools-1.3.3-py3.5.egg/EGG-INFO/requires.txt
Adding svgpathtools 1.3.3 to easy-install.pth file

Is this the intended contents of this package, or is there an error somewhere?

Happy to provide more info, but I don't know what is the intended behaviour here.

A.

Add install requirements

Is there a reason the install_requires line in setup.py is commented out? Uncommenting this would make pip automatically install dependencies.

Lacking support for paths made up of multiple subpaths

Currently, svgpathtools cannot parse/render this kind of path:

M 0,0 V 1 H 1 V 0 Z M 2,0 V 1 H 3 V 0 Z   # (1)

The issue is that the path contains two 'Z's. Svgpathtools is not equipped to remember the middle Z. It will forget it, and print this instead to the file:

M 0,0 V 1 H 1 V 0 H 0 M 2,0 V 1 H 3 V 0 Z   # (2)

But (1) renders like so:

display_temp

Whereas (2) renders like so:

display_temp

Also, svgpathtools cannot parse/render this kind of path:

M 0,0 H 1 M 1,0 V 1   # (3)

The issue is that the path contains an in-place move, which svgpathtools ignores. Svgpathtools will print this instead:

M 0,0 1,0 1,1   # (4)

But (3) renders as

display_temp

Whereas (4) renders as

display_temp

Basically, one way or the other, svgpathtools doesn't know how to properly handle paths with multiple subpaths. In fact, a "subpath" class is entirely missing.

I have been preparing a fork over at https://github.com/vistuleB/svgpathtools to remedy these issues. I hope this fork will be the object of a pull request at some point. The changes are quite extensive though and right now I am still preparing the new README for the new fork.

Interested people can take a look. The fork contains other new capabilities, such as path offsets, strokes, conversion of elliptical arcs to bezier curves, and new SaxDocument capabilities.

Now would be a good time to give me feedback, before I make the pull request.

Thanks.

Unable to read circle/ellipse when cx and cy are not specified.

Reading a file containing below svg throws error.

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-100 -100 200 200">
  <circle r="100" />
 </svg>

Below is the error.

In [5]: svgpathtools.svg2paths2('disvg_output.svg')                                                                                             
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-b24a2854f246> in <module>
----> 1 svgpathtools.svg2paths2('../red-chalice/svgtransform/disvg_output.svg')

~/Data/Projects/svgpathtools/svgpathtools/svg_to_paths.py in svg2paths2(svg_file_location, return_svg_attributes, convert_circles_to_paths, convert_ellipses_to_paths, convert_lines_to_paths, convert_polylines_to_paths, convert_polygons_to_paths, convert_rectangles_to_paths)
    213                      convert_polylines_to_paths=convert_polylines_to_paths,
    214                      convert_polygons_to_paths=convert_polygons_to_paths,
--> 215                      convert_rectangles_to_paths=convert_rectangles_to_paths)

~/Data/Projects/svgpathtools/svgpathtools/svg_to_paths.py in svg2paths(svg_file_location, return_svg_attributes, convert_circles_to_paths, convert_ellipses_to_paths, convert_lines_to_paths, convert_polylines_to_paths, convert_polygons_to_paths, convert_rectangles_to_paths)
    176     if convert_circles_to_paths:
    177         circles = [dom2dict(el) for el in doc.getElementsByTagName('circle')]
--> 178         d_strings += [ellipse2pathd(c) for c in circles]
    179         attribute_dictionary_list += circles
    180 

~/Data/Projects/svgpathtools/svgpathtools/svg_to_paths.py in <listcomp>(.0)
    176     if convert_circles_to_paths:
    177         circles = [dom2dict(el) for el in doc.getElementsByTagName('circle')]
--> 178         d_strings += [ellipse2pathd(c) for c in circles]
    179         attribute_dictionary_list += circles
    180 

~/Data/Projects/svgpathtools/svgpathtools/svg_to_paths.py in ellipse2pathd(ellipse)
     37         ry = float(ry)
     38 
---> 39     cx = float(cx)
     40     cy = float(cy)
     41 

TypeError: float() argument must be a string or a number, not 'NoneType'

SVG definition says, if cx and cy are not specified consider it as "0"
https://www.w3.org/TR/SVG11/shapes.html#CircleElementCXAttribute

I dig into the code and found that here default value should be 0

divide by zero error when computing path length

Computing length of each path returned by svg2paths() fails when reading the following svg:
https://www.dropbox.com/s/2b13vqykmvid0eg/13865.svg?dl=0

Procedure to reproduce:

from svgpathtools import svg2paths
paths, path_attrs = svg2paths('13865.svg', return_svg_attributes=False)
for path_id, path in enumerate(paths): 
    path_attr = path_attrs[path_id]
    plen = int(path.length())

The error it throws:

/usr/local/lib/python3.5/dist-packages/svgpathtools/path.py in length(self, T0, T1, error, min_depth)
   1899 
   1900     def length(self, T0=0, T1=1, error=LENGTH_ERROR, min_depth=LENGTH_MIN_DEPTH):
-> 1901         self._calc_lengths(error=error, min_depth=min_depth)
   1902         if T0 == 0 and T1 == 1:
   1903             return self._length

/usr/local/lib/python3.5/dist-packages/svgpathtools/path.py in _calc_lengths(self, error, min_depth)
   1876                    self._segments]
   1877         self._length = sum(lengths)
-> 1878         self._lengths = [each/self._length for each in lengths]
   1879 
   1880     def point(self, pos):

/usr/local/lib/python3.5/dist-packages/svgpathtools/path.py in <listcomp>(.0)
   1876                    self._segments]
   1877         self._length = sum(lengths)
-> 1878         self._lengths = [each/self._length for each in lengths]
   1879 
   1880     def point(self, pos):

ZeroDivisionError: float division by zero

The error seems to come from self._length being zero in path.py. It is unusual but possible as the sketch above has a path:
'M261 166 L261 166 '
which looks like a single dot on the canvas.

Adding an epsilon to self._length seems to be a workaround:
self._length = sum(lengths) + np.finfo(np.float32).eps

svg2paths not recognizing ellipses

I haven't yet dived into why this is the case, but this svg generates empty lists for both the attributes and paths returned values for svg2paths. I'll try to look deeper into this later today.

<svg width="262" height="178" xmlns="http://www.w3.org/2000/svg"> <ellipse ry="51" rx="51" id="svg_3" cy="101" cx="77.5" stroke-width="1.5" stroke="#00ffff" fill="none"/> <ellipse ry="57.5" rx="57.5" id="svg_4" cy="100.5" cx="145" stroke-width="1.5" stroke="#ff0000" fill="none"/> </svg>

Decouple the Parser from the Path

As is the parser for the SVG paths instances the Path object and the Line objects and the curve objects. It would be better if it didn't do that. Passing the path object into the parse_path command and as it parses the commands it would call a command on path that add_line() or add_curve() or start(), or calling these on a different class that simple delegated to segments.append(Line(start_pos,end_pos)) or whatever.

It would make that code rather independently useful, as anybody could cook up a parser object and throw it in there, even if they weren't going to actually use the rest of the path tools as they were intended. The code has some reasonable svg parse capabilities and those could be broken off and decoupled more effectively from the math commands and logic in the primary classes.

This could even be taken to the somewhat logical extreme of branching that project off completely and writing an svgread module, which would be completely ambiguous as to where the data ends up. Just including the parsing tools as such loading the data in either a proper dom or the various other methods of loading and extracting that data. Including the svg path parsing. Which would basically yield proper calls parse that data. As is the intersection between the parser bits, the svgpathtools and svgwrite all seems kind of sloppy.

First pull request of my life, pls help

Hi, I've never contributed to an open source project before, I hardly know how to use git. (Mathematician here.)

Could someone guide me through my first pull request?

The modifications I've made (small to big)

-- correct bug in path_encloses_pt [current bug: double-counts intersections at segment endpoints]

-- add a "multisplit" functionality on top of "split" to paths

-- add a feature for approximating arc segments by a sequence of cubic bezier curves... so now any path can be turned into a purely bezier path... the user can control the accuracy of the approximation, as well as well as the "safety" (number of points measured along the curve to ensure said accuracy is respected)... (The cubic beziers I used are those described in the paper "Drawing an Elliptical Arc using Polylines, Quadratic or Cubic Bezier Curves" by L. Maisonobe.)

-- (the above could also be used for measuring area of paths with arc segments, though I didn't make the change since I don't need area myself)

-- path offsets: offset a path by some amount; exact offset is not possible for bezier curves (or arcs), so the user gets to choose an accuracy, and bezier curves are recursively (and adaptively) subdivided into smaller bezier curves (of same degree) until the desired accuracy is respected everywhere; accurately produces cusps, etc, for concave offsets; requires the "arc to bezier" transformation above for paths with elliptical arcs (i.e., elliptical arcs are first transformed into a sequence of cubic beziers, after which these are offset)

-- joins: afore-mentioned path offset accommodates the standard SVG segment joins: miter, bevel, and round; miter-limit also implemented

-- path stroke: two-sided path offset with added line caps and line cap options 'butt', 'square' and 'round'---basically, now one can implement a "stroke to path" operation, as found in popular software like inkscape in 1 command, without resorting to approximation by polylines, and with all the standard line cap/segment join SVG options

-- also works with discontinuous and/or closed paths and/or mixtures thereof :)

-- fun fact: the above offset and stroke operations are robust enough to be used with negative offsets / stroke widths... in such a case the endcaps of the path take an inverted shape, as expected.

I'd like to contribute all this to the repo, but will need a little bit of guidance... thanks...

bug in Path.area()

I'm seeing this problem on the current tip of master, ae42197.

from svgpathtools.path import *
p = Path(Arc(start=(106.58928+132.95833j), radius=(40.82143+83.910713j), rotation=0.0, large_arc=False, sweep=True, end=(74.472411+214.93917j)),
     Arc(start=(74.472411+214.93917j), radius=(40.82143+83.910713j), rotation=0.0, large_arc=False, sweep=True, end=(28.658655+167.9207j)),
     Arc(start=(28.658655+167.9207j), radius=(40.82143+83.910713j), rotation=0.0, large_arc=False, sweep=True, end=(41.237335+65.887886j)),
     Line(start=(41.237335+65.887886j), end=(65.767853+132.95833j)),
     Line(start=(65.767853+132.95833j), end=(106.58928+132.95833j)))
p.area()

This hangs until I interrupt it with ^C, then I get this backtrace:

KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-4-38eb451037da> in <module>()
----> 1 p.area()

/home/seb/svg2gcode/svgpathtools/svgpathtools/path.pyc in area(self, chord_length)
   2568             else:
   2569                 bezier_path_approximation.append(seg)
-> 2570         return area_without_arcs(Path(*bezier_path_approximation))
   2571 
   2572     def intersect(self, other_curve, justonemode=False, tol=1e-12):

/home/seb/svg2gcode/svgpathtools/svgpathtools/path.pyc in area_without_arcs(path)
   2548             for seg in path:
   2549                 x = real(seg.poly())
-> 2550                 dy = imag(seg.poly()).deriv()
   2551                 integrand = x*dy
   2552                 integral = integrand.integ()

/home/seb/svg2gcode/svgpathtools/svgpathtools/polytools.pyc in imag(z)
     66 def imag(z):
     67     try:
---> 68         return np.poly1d(z.coeffs.imag)
     69     except AttributeError:
     70         return z.imag

/usr/lib/python2.7/dist-packages/numpy/lib/polynomial.pyc in __init__(self, c_or_r, r, variable)
   1054         if len(c_or_r.shape) > 1:
   1055             raise ValueError("Polynomial must be 1d only.")
-> 1056         c_or_r = trim_zeros(c_or_r, trim='f')
   1057         if len(c_or_r) == 0:
   1058             c_or_r = NX.array([0.])

/usr/lib/python2.7/dist-packages/numpy/lib/function_base.pyc in trim_zeros(filt, trim)
   2097             else:
   2098                 last = last - 1
-> 2099     return filt[first:last]
   2100 
   2101 

KeyboardInterrupt: 

No Support for SVG images using defs

The following code is taken from the README. I'm attempting to parse SVG images and then reconstruct them without any losses.

from svgpathtools import svg2paths2, wsvg
paths, attributes, svg_attributes = svg2paths2('def_ex.svg')

# Let's make a new SVG that's identical to the first
wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename='def_ex_processed.svg')

When parsed the picture is modified, images below:

Original Image

<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <!-- Some graphical objects to use -->
  <defs>
    <circle id="myCircle" cx="0" cy="0" r="5" />
  </defs>
 
  <!-- using my graphical objects -->
  <use x="5" y="5" xlink:href="#myCircle"/>
</svg>

Reconstructed Image

<?xml version="1.0" ?>
<svg baseProfile="full" height="600px" version="1.1" viewBox="0 0 10 10" 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">
	<defs/>
	<path cx="0" cy="0" d="M -5.0,0.0 A 5.0,5.0 0.0 1,0 5.0,0.0 A 5.0,5.0 0.0 1,0 -5.0,0.0" id="myCircle" r="5"/>
</svg>

Sorry If this can't easily be viewed I'm not sure of the best way to post SVG images to issues.

The images loses its positioning which is important for what I'm trying do. I believe the issue is occurring with the line <use x="5" y="5" xlink:href="#myCircle"/> where svg2paths2 is unable to understand the reference to the myCircle def from before

Relevant Docs on defs in SVG

Reason for using complex numbers to store coordinates

I am not sure this is an issue exactly but is there a reason complex numbers are used to store the coordinates of points? It would make sense if it was converted to polar coordinates but unless I am misunderstanding things, it is using Cartesian and the real number is the X and the imaginary is the Y.

I think this is a holdover from the adaptation from the svg.path but I wanted to do some more work concerning the points and found it easier to just replace the complex numbers with a simple point class that has some convenience functions. My fork: https://github.com/brinnLabs/svgpathtools

Iterate split on 4 continuous Arc fails

Hi,

when I split iteratly a circle (if it is 4 Arcs for example - no problem if it's Bezier Curve), I obtain this error : math domain error
The problem is caused by acos() in parameterize function in Path.py
circle_arcsx4.svg.txt

. Value for u1.real can be -1.000000002 and it's the problem...

My solution : add this function in misctools (it should be useful everywhere...) :

#-- constrain(x,a,b)
def constrain(x,valMin,valMax):
if x < valMin :
return valMin

elif valMax < x :
    return valMax

else :
    return x

Import it in path.py :
from .misctools import BugException, constrain # added XH

And in def _parameterize, change :

    if u1.imag > 0:
        self.theta = degrees(acos(constrain(u1. real,-1.0, 1.0))) # acos need -1<=value<=1 - added XH
    elif u1.imag < 0:
        self.theta = -degrees(acos(constrain(u1. real,-1.0, 1.0))) # acos need -1<=value<=1 - added XH

And it's work well to iterate split on circle !

Test file joined. (remove .txt to obtain .svg - github seems not support svg file joined...)

Xavier HINAULT
France

subpaths do not need to start with M/m

For example m0 0h50v50zv20h20z right now causes an error:

Traceback (most recent call last):
File "", line 1, in
File "C:\python36\lib\site-packages\svgpathtools-1.3.2-py3.6.egg\svgpathtools\parser.py", line 81, in parse_path
File "C:\python36\lib\site-packages\svgpathtools-1.3.2-py3.6.egg\svgpathtools\path.py", line 1883, in closed
ValueError: End does not coincide with a segment start.

But this is ok

If a "closepath" is followed immediately by any other command, then the next subpath starts at the same initial point as the current subpath.

Approximation Problem

Hi,

A Path object contain for example :

CubicBezier(start=(27.080179592571874+157.03434674518752j), control1=(27.61140677353281+155.774203506625j), control2=(28.261543320386714+154.57737991657814j), end=(29.016691803786323+153.45777340439065j)),
CubicBezier(start=(29.016691803786323+153.45777340439065j), control1=(29.771840287185935+152.33816689220313j), control2=(30.632000707131247+151.295777457875j), end=(31.583275634274997+150.34450253075j)),
CubicBezier(start=(31.583275634274997+150.34450253075j), control1=(32.53455056141875+149.393227603625j), control2=(33.57693999576094+148.53306718370314j), end=(34.696546507954295+147.77791870032814j)),
CubicBezier(start=(34.696546507954295+147.77791870032814j), control1=(35.81615302014765+147.02277021695315j), control2=(37.01297661019218+146.37263367012503j), end=(38.27311984874062+145.8414064891875j)),
CubicBezier(start=(38.27311984874062+145.8414064891875j), control1=(39.53326308728906+145.31017930824999j), control2=(40.8567259743414+144.89786149320312j), end=(42.229611080550384+144.61835047339065j)),
CubicBezier(start=(42.229611080550384+144.61835047339065j), control1=(43.60249618675937+144.33883945357815j), control2=(45.024803512125+144.192135229j), end=(46.4826356273+144.192135229j)))]

I want to reuse the start and end point from each object to build Line object with :
spt.Line(elem.point(0), elem.point(1))

where elem is an element in Path source

I obtain :

 Line(start=(27.080179592571874+157.03434674518752j), end=(29.01669180378633+153.45777340439054j)),
 Line(start=(29.016691803786323+153.45777340439065j), end=(31.58327563427502+150.34450253075013j)),
 Line(start=(31.583275634274997+150.34450253075j), end=(34.69654650795428+147.77791870032806j)),
 Line(start=(34.696546507954295+147.77791870032814j), end=(38.27311984874062+145.84140648918756j)),
 Line(start=(38.27311984874062+145.8414064891875j), end=(42.2296110805504+144.61835047339065j)),
 Line(start=(42.229611080550384+144.61835047339065j), end=(46.48263562729995+144.1921352290001j)))]

As you can see, there is a little difference :
29.016691803786323+153.45777340439065j give 29.01669180378633+153.45777340439054j
31.58327563427502+150.34450253075013j give 31.583275634274997+150.34450253075j
46.4826356273+144.192135229j give 46.48263562729995+144.1921352290001j

Only end point seem have this problem.

Logically, Path() obtained is not continuous, etc.

Is it possible to give a tolerance for continuous or limit precision to 6 or 8 decimal for example ?

Thanks in advance for your suggest.

Path.intersect() withholds some intersections from the result due to a typo

With the following shapes intersecting in 2 places:


from svgpathtools.parser import parse_path

# clipping rectangle
p1 = parse_path('M0.0 0.0 L27.84765625 0.0 L27.84765625 242.6669922 L0.0 242.6669922 z')

# frame with curved corners
p2 = parse_path('M166.8359375,235.5478516c0,3.7773438-3.0859375,6.8691406-6.8701172,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805c3.7841797,0,6.8701172,3.0927734,6.8701172,6.8701172v228.4277344z')

I see this REPL output:


>>> print(len(p1.intersect(p2)))
2
>>> print(len(p2.intersect(p1)))
1

This seems to be caused by Path.intersect() removing some intersections from the result due to a typo. If I patch svgpathtools/path.py version 1.3.3 at line 2196 as follows:

<            pts = [seg1.point(_t1) for _T1, _seg1, _t1 in list(zip(*intersection_list))[0]]
---
>            pts = [_seg1.point(_t1) for _T1, _seg1, _t1 in list(zip(*intersection_list))[0]]

I get the expected result:


>>> print(len(p1.intersect(p2)))
2
>>> print(len(p2.intersect(p1)))
2

BTW, it would be nice to make a new release of svgpathtools and pick up all bug fixes accumulated since last summer, including a fix for this issue :)

a selfish request for credit.

Heylo,

thanks for your comment on my Primer on Bézier Curves, it's always great to see an application for what I've documented, but I noticed your README.md has a section called "Credit where credit's due" - while you of course don't need to mention the primer by URL, it might be useful for others as a further reference link they can visit and sink their teeth into =)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.