Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast way to sample geometric points from SVG paths

Tags:

python

svg

I'm using the excellent svgpathtools library in Python 3 to work with some paths in an SVG file, created in a vector drawing application.

I'd like to create detailed point arrays for each of the paths contained within the SVG, where the points are equidistant along the path. The following does just that but becomes unbearably slow if more than a few thousand samples are taken.

SAMPLES_PER_PX = 1

fname = "/path/to/file.svg"
paths, attributes = svg2paths(fname)

myPaths = {}
for path,attr in zip(paths, attributes):
    myPathList = []
    pathLength = path.length()
    pathColour = attr['stroke']
    numSamples = int(pathLength * SAMPLES_PER_PX)
    for i in range(numSamples):
        #parametric length = ilength(geometric length)
        myPathList.append(path.point(path.ilength(pathLength * i / (numSamples-1))))
    myPaths[pathColour] = np.array(myPathList)

I've always felt that my Python ain't very Pythonic. Is there a way I can take advantage of some Python-ness to speed this up?

like image 982
Heath Raftery Avatar asked Oct 21 '25 18:10

Heath Raftery


2 Answers

I had the same problem. My solution was to sample N points using path.point and then interpolate those points using scipy spline and resample from the spline. Something like:

tck, _ = interpolate.splprep(pts, s=0)
result = interpolate.splev(np.linspace(0, 1, 1000), tck)

Where pts is a list of sampled points. The number N depends on how non-uniform the input curve is. Usually N=20 is sufficient for not too crazy cases:-)

like image 60
user3469029 Avatar answered Oct 23 '25 08:10

user3469029


You actually do not need to use the ilength method. What you are doing with myPathList.append(path.point(path.ilength(pathLength * i / (numSamples-1)))) is computing the position of the point you want on the geometric length of the path, but since you know you are taking numSamples points regularly spaced on the path, you will keep the same number of points regularly spaced in the parametric distance, so you can directly write myPathList.append(path.point(i / (numSamples-1))). Hope this will help!

like image 35
Wes B. Avatar answered Oct 23 '25 08:10

Wes B.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!