Created
August 31, 2021 07:13
-
-
Save simonecesano/11c04cc65fcaa42acd3a32440c041304 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from svgpathtools import parse_path, kinks | |
import numpy as np | |
# a bunch of paths - atually all quite similar | |
paths = [ | |
'm 269.68858,24.762486 c -40.50864,-1.700513 -82.13567,24.752876 -90.53902,65.804462 -13.14294,37.392562 -17.2298,77.029262 -21.30965,116.163222 2.18009,35.53756 2.00517,72.32618 18.49409,105.0151 17.65672,49.88028 40.60061,89.34487 56.23634,137.55913 8.01979,33.12323 -0.64042,66.17131 0.0854,99.31004 -1.70152,58.80692 -18.69271,117.97032 -3.57251,176.35988 4.75221,41.18507 47.61108,68.96937 84.43863,69.35203 36.82756,0.38265 66.60039,-18.16206 77.87482,-50.2879 17.90406,-42.01272 17.79235,-81.49902 16.42076,-133.65857 -1.37159,-52.15955 6.43528,-134.61422 11.35889,-178.16317 4.9236,-43.54895 9.02792,-54.92485 8.65922,-82.92663 C 434.67597,283.85003 417.4602,218.83964 391.752,159.17256 381.13197,135.51373 372.07804,115.94824 355.87316,89.293903 339.66828,62.639564 309.19251,27.626442 269.68858,24.762486 Z', | |
'm 269.903,19.8186 c -11.91711,-0.51685 -32.27731,1.070319 -53.46034,13.363287 -7.57964,4.398624 -15.09441,10.115707 -21.7639,17.309543 -6.10547,6.585479 -11.48984,14.405852 -15.42262,23.411503 -1.88956,4.341093 -3.20655,8.762796 -4.45743,13.203647 -0.006,0.02087 -0.16665,0.593017 -0.17253,0.613887 -0.92526,3.298648 -2.13708,7.795125 -3.58284,11.703021 -0.0335,0.0905 -0.0643,0.181961 -0.0924,0.274263 -10.08152,33.076419 -13.8021,65.940699 -18.03284,106.519239 -0.0282,0.27038 -0.0341,0.54266 -0.0175,0.814 0.98132,16.11317 1.53474,33.95368 3.54713,50.93551 2.03496,17.17236 5.60753,34.19583 12.80558,50.3898 0.01,0.0224 0.02,0.0446 0.0303,0.0669 2.63691,5.70301 4.88806,11.66869 7.07194,17.50312 2.39713,6.40418 4.5705,12.24991 7.19584,18.13489 8.15411,19.54232 16.41702,37.29627 23.38065,53.17688 10.17192,23.19711 16.22795,38.97872 20.92776,53.58235 a 4.9485337,4.9485337 90 0 0 9.4212,-3.03198 c -4.83331,-15.01844 -11.02505,-31.12699 -21.28502,-54.5249 -7.02031,-16.00985 -15.28013,-33.76328 -23.33392,-53.06904 -0.0157,-0.0377 -0.032,-0.0753 -0.0486,-0.11262 -2.4887,-5.57281 -4.56,-11.13612 -6.98887,-17.62505 -2.16627,-5.78741 -4.53371,-12.07537 -7.34197,-18.15331 -6.63971,-14.94999 -10.03852,-30.89399 -12.00657,-47.50167 -1.9519,-16.47139 -2.48119,-33.55744 -3.4719,-49.96405 4.19097,-40.19716 7.83789,-72.12408 17.5727,-104.11915 1.59772,-4.346868 2.91877,-9.259422 3.77871,-12.325233 -0.007,0.02482 0.17656,-0.62828 0.1696,-0.603457 1.2399,-4.401869 2.40609,-8.261978 4.00337,-11.931579 3.44767,-7.894808 8.18241,-14.785879 13.6081,-20.638137 5.93027,-6.396497 12.64887,-11.517647 19.47371,-15.47825 19.10508,-11.087085 37.44871,-12.495977 48.06391,-12.035592 Z', | |
'm 317.49193,24.762486 c 40.50864,-1.700513 82.13567,24.752876 90.53902,65.804462 13.14294,37.392562 17.2298,77.029262 21.30965,116.163222 -2.18009,35.53756 -2.00517,72.32618 -18.49409,105.0151 -17.65672,49.88028 -40.60061,89.34487 -56.23634,137.55913 -8.01979,33.12323 0.64042,66.17131 -0.0854,99.31004 1.70152,58.80692 18.69271,117.97032 3.57251,176.35988 -4.75221,41.18507 -47.61108,68.96937 -84.43863,69.35203 -36.82756,0.38265 -66.60039,-18.16206 -77.87482,-50.2879 -17.90406,-42.01272 -17.79235,-81.49902 -16.42076,-133.65857 1.37159,-52.15955 -6.43528,-134.61422 -11.35889,-178.16317 -4.9236,-43.54895 -9.02792,-54.92485 -8.65922,-82.92663 -6.84042,-65.44005 10.37535,-130.45044 36.08355,-190.11752 10.62003,-23.65883 19.67396,-43.22432 35.87884,-69.878657 16.20488,-26.654339 46.68065,-61.667461 86.18458,-64.531417 z', | |
'M 43.179732,261.02847 C 43.179732,194.89491 43.179732,194.89491 109.1273,194.89491', | |
'm 43.179732,261.02847 c 0,-66.13356 60.431218,-66.13356 65.947568,-66.13356', | |
] | |
path = parse_path(paths[0]) | |
print("Parsed path is %d long\n" % path.length()) | |
print("Parsed path has %d segments\n" % len(path)) | |
hpoints = [] # points where tangent is horizontal | |
vpoints = [] # points where tangent is vertical | |
i=0 | |
# the path is made up of segments | |
for s in path: | |
# convert segment to numpy polynomial for later use | |
p = s.poly() | |
print("### Segment %d\n" % i) | |
i += 1 | |
# print segment as python object | |
print("#### Python object\n\n " + str(s) + "\n") | |
# prettify equation | |
bpretty = "{}*(1-t)^3 + 3*{}*(1-t)^2*t + 3*{}*(1-t)*t^2 + {}*t^3".format(*s.bpoints()) | |
# print it, and print its standard format | |
print("#### CubicBezier\n\n b.point(x) = " + | |
bpretty + "\n\n" + | |
"#### Standard form\n\n " + | |
str(p).replace('x','t').replace("\n","\n ") + "\n") | |
print("---------------------------------") | |
# find roots - in this case of the derivative | |
# this is the part that needs figuring out | |
# numpy treats 2d polynomials as polynomials with complex coefficients. Since | |
# you need to find the roots of the two coordinates seperately, you separate | |
# the single complex polynomial into two separate ones, one containing the | |
# real part of the coefficients, the other one containing the imaginary part. | |
p1 = np.poly1d(np.real(p)) | |
p2 = np.poly1d(np.imag(p)) | |
# take the derivative of each of the two polynomials | |
d1 = p1.deriv() | |
d2 = p2.deriv() | |
# obtain points where tangent is horizontal (in terms of the coordinate | |
# used to parametrize, that is "t") by imposing the y coordinate of the | |
# derivative to be equal to 0 | |
hpoints_raw = np.roots(d2) | |
# same thing but now the x coordinate is equal to 0 | |
vpoints_raw = np.roots(d1) | |
for t in hpoints_raw: | |
if np.isreal(t): # check if solution is real | |
if 0 <= t and t <= 1: # check if t is within bounds | |
# by evaluating p1 and p2 at the value of t found, you are converting | |
# from parametrized coordinates to the original coordinates | |
hpoints.append((p1(t), p2(t))) | |
for t in vpoints_raw: | |
if np.isreal(t): | |
if 0 <= t and t <= 1: | |
vpoints.append((p1(t), p2(t))) | |
print("---------------------------------") | |
print("horizontal points: ", hpoints) | |
print("vertical points: ", vpoints) | |
print("---------------------------------") | |
// ------------------------------------------------------ | |
// saving the SVG | |
// ------------------------------------------------------ | |
import svgwrite | |
import os | |
if os.path.exists('test_02.svg'): os.remove('test_02.svg') | |
dwg = svgwrite.Drawing('test_02.svg') | |
dwg.add(dwg.path(d=paths[0],stroke=svgwrite.rgb(0, 0, 0, '%'), fill="#cccccc")) | |
for p in vpoints: | |
print(p[0], p[1]) | |
dwg.add(dwg.circle(center=(p[0], p[1]), r=12, stroke=svgwrite.rgb(0, 0, 0, '%'), fill="#880000")) | |
for p in hpoints: | |
print(p[0], p[1]) | |
dwg.add(dwg.circle(center=(p[0], p[1]), r=12, stroke=svgwrite.rgb(0, 0, 0, '%'), fill="#000088")) | |
dwg.save(pretty=True, indent=2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment