The Inkscape SVG editor has some neat path manipulation tools built in. One I'm particularly interested in accessing programmatically is the offset function, which (attempts to) create a path a fixed distance from an existing path, as depicted here (the black lines are offsets of the red line):
I'd like to be able to perform this operation from a Python program.
Inkscape has rudimentary scripting support, but it basically only consists of calling non-interactive menu commands - For example, you can create a path that's inset or outset from an existing path, but only by exactly 1px or 10px, not by a user-specified amount. So that doesn't seem useful here.
Is there a library or other tool from which I can do these sorts of path transformations (ideally to an SVG file) in Python?
There's a problem with this. You can create a visual approximation (or a path approximating) the offset path, but the offset curve of a Bezier curve or elliptic arc will not in general be a Bezier curve or elliptic arc.
That said, there are explicit instructions how to create a piecewise-linear approximation of such an offset curve in the README of the svgpathtools python module (just follow the link and scroll down - it's the last example, "An Advanced Application: Offsetting Paths").
Here's the code:
from svgpathtools import parse_path, Line, Path, wsvg
def offset_curve(path, offset_distance, steps=1000):
"""Takes in a Path object, `path`, and a distance,
`offset_distance`, and outputs an piecewise-linear approximation
of the 'parallel' offset curve."""
nls = []
for seg in path:
ct = 1
for k in range(steps):
t = k / steps
offset_vector = offset_distance * seg.normal(t)
nl = Line(seg.point(t), seg.point(t) + offset_vector)
nls.append(nl)
connect_the_dots = [Line(nls[k].end, nls[k+1].end) for k in range(len(nls)-1)]
if path.isclosed():
connect_the_dots.append(Line(nls[-1].end, nls[0].end))
offset_path = Path(*connect_the_dots)
return offset_path
# Examples:
path1 = parse_path("m 288,600 c -52,-28 -42,-61 0,-97 ")
path2 = parse_path("M 151,395 C 407,485 726.17662,160 634,339").translated(300)
path3 = parse_path("m 117,695 c 237,-7 -103,-146 457,0").translated(500+400j)
paths = [path1, path2, path3]
offset_distances = [10*k for k in range(1,51)]
offset_paths = []
for path in paths:
for distances in offset_distances:
offset_paths.append(offset_curve(path, distances))
# Note: This will take a few moments
wsvg(paths + offset_paths, 'g'*len(paths) + 'r'*len(offset_paths), filename='offsetcurves.svg')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With