mathandy / svgpathtools

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

divide by zero error when computing path length #95

Closed TuBui closed 3 years ago

TuBui commented 5 years ago

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

tatarize commented 4 years ago

You wouldn't work around this bug. 0 is a correct length. Return zero. Do not try to average things out of self._length == 0 then it is the case that they all equal zero and the average is zero. The other problem here is that with zero lengths it means calculation of your position within that path also suffers a different error for that edge condition that needs to be fixed.

path.point(0.5) gives you a different kind of crash since you're half way through 0 length.