mathandy / svgpathtools

A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.
MIT License
557 stars 142 forks source link

isclosed() suffers from rounding errors when reading paths #198

Closed xmarduel closed 1 year ago

xmarduel commented 1 year ago

when reading such a svg file:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="40mm" height="40mm" viewBox="0 0 40 40" version="1.1">

  <g id="layer">
    <circle id="c1" cx="20.000" cy="20.000" r="11.000" style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.352776" />
    <circle id="c2" cx="20.000" cy="20.000" r="5.15" style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.352776;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000" />
  </g>
</svg>

the svg2paths functions return 2 paths (of course) for the two circles. Unfortunately, because of float rounding, svgpathtools says that the first circle is closed, but not the second one: it 's clear why:

>>> p, attrs = svgpathtools.svg2paths("circles.svg")
>>> p
[Path(Arc(start=(9+20j), radius=(11+11j), rotation=0.0, large_arc=True, sweep=False, end=(31+20j)),
     Arc(start=(31+20j), radius=(11+11j), rotation=0.0, large_arc=True, sweep=False, end=(9+20j))), 
Path(Arc(start=(14.85+20j), radius=(5.15+5.15j), rotation=0.0, large_arc=True, sweep=False, end=(25.15+20j)),
     Arc(start=(25.15+20j), radius=(5.15+5.15j), rotation=0.0, large_arc=True, sweep=False, end=(14.849999999999998+20j)))]
>>> p[0].isclosed()
True
>>> p[1].isclosed()
False

So yes, for the second circle, 14.849999999999998 != 14.85 so the initial point is not the end point, and for svgpathtools logic it is unfortunately suffisant to declare that the circle is not closed.

mathandy commented 1 year ago

Yes. This is a bug.

mathandy commented 1 year ago

fixed

mathandy commented 1 year ago

And thank you!