Skip to content

Instantly share code, notes, and snippets.

@migurski
Created May 1, 2022 21:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save migurski/5540db571365921eb6c9ed8bc9f00219 to your computer and use it in GitHub Desktop.
Save migurski/5540db571365921eb6c9ed8bc9f00219 to your computer and use it in GitHub Desktop.
import re
import sys
import numpy
import shapely.geometry
import shapely.ops
import bezier
moveto_pat = re.compile(r'^M(?P<x>-?\d+(\.\d+(e-?\d+)?)?),(?P<y>-?\d+(\.\d+(e-?\d+)?)?)(\s+(?P<tail>.+))?$')
lineto_pat = re.compile(r'^L(?P<x>-?\d+(\.\d+(e-?\d+)?)?),(?P<y>-?\d+(\.\d+(e-?\d+)?)?)(\s+(?P<tail>.+))?$')
curveto_pat = re.compile(r'''
^C(?P<x1>-?\d+(\.\d+(e-?\d+)?)?),(?P<y1>-?\d+(\.\d+(e-?\d+)?)?) # first control point
\s+(?P<x2>-?\d+(\.\d+(e-?\d+)?)?),(?P<y2>-?\d+(\.\d+(e-?\d+)?)?) # second control point
\s+(?P<x>-?\d+(\.\d+(e-?\d+)?)?),(?P<y>-?\d+(\.\d+(e-?\d+)?)?) # end point
(\s+(?P<tail>.+))?$
''', re.VERBOSE)
closepath_pat = re.compile(r'^Z(\s+(?P<tail>.+))?$')
assert moveto_pat.match('M1,2')
assert moveto_pat.match('M1.1,2.2')
assert moveto_pat.match('M1.1e-1,2.2')
assert lineto_pat.match('L1,2')
assert lineto_pat.match('L1.1,2.2')
assert lineto_pat.match('L1.1,2.2e-1')
def parse_path_new(path, resolution):
'''
'''
pass
def parse_path(path, resolution):
''' Parse an SVG path:d, assume it's made up of curved and straight polygons
https://www.w3.org/TR/SVG/paths.html#DProperty
'''
while path:
moveto = moveto_pat.match(path)
if not moveto:
raise StopIteration()
path = moveto.group('tail')
x, y = [round(float(moveto.group(g))) for g in 'xy']
coords = [(x, y)]
while path:
closepath = closepath_pat.match(path)
if closepath:
path = closepath.group('tail')
yield coords
break
lineto = lineto_pat.match(path)
if lineto:
path = lineto.group('tail')
x, y = [round(float(lineto.group(g))) for g in 'xy']
coords.append((x, y))
curveto = curveto_pat.match(path)
if curveto:
path = curveto.group('tail')
xys = [round(float(curveto.group(g))) for g in ('x1', 'y1', 'x2', 'y2', 'x', 'y')]
nodes = numpy.array([
coords[-1],
(xys[0], xys[1]),
(xys[2], xys[3]),
(xys[4], xys[5]),
])
curve = bezier.Curve.from_nodes(nodes.transpose())
range = numpy.linspace(0, 1, int(curve.length / resolution) + 2)
points = curve.evaluate_multi(range).transpose()
coords.extend(points[1:,:].tolist())
def path_multipolygon(path, resolution):
rings = [
shapely.geometry.Polygon(line)
for line in parse_path(path, resolution)
]
polygons = []
for poly in shapely.ops.polygonize(rings):
if not poly.is_valid:
pass # print(poly, file=sys.stderr)
touches = [
poly.relate_pattern(other, '****1****')
for other in polygons
]
if True not in touches:
polygons.append(poly)
return shapely.geometry.MultiPolygon(polygons)
if __name__ == '__main__':
d = '''M500.04,309.630308 C817.696558,309.630308 931.823304,522.666719 936.58,532.180112 L936.58,549.295931 C931.824662,557.855539 817.696558,770.894667 500.04,770.894667 C182.383442,770.894667 68.2566964,557.858256 63.5,548.344863 L63.5,531.229044 C68.2553377,522.669436 182.383442,309.630308 500.04,309.630308 Z M520.015815,422.809043 L520.015815,422.806648 C524.766058,399.02996 514.304315,374.305261 494.342086,360.983862 L494.342086,360.035189 C475.315641,360.991352 456.295139,367.645088 440.12784,378.107171 C424.909061,388.570612 412.543655,402.840022 403.985575,419.955841 C407.793242,444.686993 425.863525,464.656354 450.589583,469.413051 C482.924181,476.067127 513.355115,455.143641 520.015815,422.809043 Z M357.379869,438.976342 L357.373076,438.975833 C364.033945,404.737401 383.053598,375.254308 409.690282,354.330822 C375.451851,359.086159 344.064924,367.644748 316.487362,377.158141 C303.173265,404.735703 295.563876,436.12263 295.563876,467.507859 C295.563876,580.681499 386.864661,671.987379 500.043397,671.987379 C613.217037,671.987379 704.522917,580.686594 704.522917,467.507859 C704.522917,436.12263 696.914037,404.735703 683.599432,377.158141 C656.015076,367.645428 625.584312,359.087518 591.34588,354.330822 C602.755294,363.8381 612.269196,374.299504 620.827276,386.66542 C642.700131,418.052347 650.30952,457.047814 642.700131,495.087119 C635.098384,533.126424 613.218735,565.467815 580.884137,587.34067 C556.151286,603.508649 528.573724,612.066728 500.04,612.066728 C490.525928,612.066728 481.019668,611.118038 471.506276,609.214035 C433.466971,601.612288 401.125579,578.784969 379.252724,547.398041 C357.379869,516.011114 349.77048,477.015647 357.379869,438.976342 Z M102.493769,539.787803 L102.491901,539.784406 C119.611117,568.318131 181.42711,653.917606 309.824624,700.516518 C242.302226,644.403534 198.549722,560.711289 198.549722,466.553904 C198.549722,457.048324 199.505053,446.585901 200.453725,437.072509 C144.342439,477.97181 113.904542,521.715821 102.493769,539.787803 Z M691.204575,700.519915 L691.202707,700.523312 C818.654249,653.924399 880.470242,568.324924 898.53543,539.7912 C887.124318,520.771547 856.68676,477.968413 800.575474,437.075905 C801.530804,446.581486 802.479477,457.043908 802.479477,466.557301 C802.479477,560.712987 758.733767,645.357998 691.204575,700.519915 Z M500.04,267.783336 L500.036603,267.783336 C304.11669,267.783336 182.383442,338.164032 114.85425,397.135315 L90.1281927,368.60159 C162.409326,304.873272 291.754511,229.742333 500.038302,229.742333 C708.322093,229.742333 837.668976,304.873272 909.948411,368.594797 L885.222353,397.128521 C817.696558,338.164032 695.959913,267.783336 500.04,267.783336 Z'''
p = path_multipolygon(d, 10)
print(p.wkt)
print(p.is_valid, file=sys.stderr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment