diff --git a/docs/calle.svg b/docs/calle.svg new file mode 100644 index 0000000..06a6986 --- /dev/null +++ b/docs/calle.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/calle.py b/tools/calle.py new file mode 100644 index 0000000..3edfc2b --- /dev/null +++ b/tools/calle.py @@ -0,0 +1,119 @@ +# Genera docs/calle.svg: ilustración stencil "a la calle" +import sys, random +from fontTools.ttLib import TTFont +from fontTools.pens.svgPathPen import SVGPathPen +from fontTools.pens.transformPen import TransformPen +random.seed(26) +FONT, OUT = sys.argv[1], sys.argv[2] +W, H = 1200, 640 +INK, RED = "#000", "#ff1f1f" +out = [] +def add(s): out.append(s) + +def text_path(txt, x, y, size, fill, anchor="middle", font=FONT): + f = TTFont(font); gs = f.getGlyphSet(); cmap = f.getBestCmap() + upm = f["head"].unitsPerEm; k = size / upm + widths = [f["hmtx"][cmap[ord(c)]][0] for c in txt] + total = sum(widths) * k + cx = x - total / 2 if anchor == "middle" else x + d = [] + for c, w in zip(txt, widths): + pen = SVGPathPen(gs) + gs[cmap[ord(c)]].draw(TransformPen(pen, (k, 0, 0, -k, cx, y))) + d.append(pen.getCommands()); cx += w * k + return f'' + +def blob(cx, cy, r, n, jit, fill, op=1): + import math + pts = [] + for i in range(n): + a = 2 * math.pi * i / n + rr = r * (1 + random.uniform(-jit, jit)) + if random.random() < .25: rr *= 1.25 + pts.append((cx + rr * math.cos(a), cy + rr * math.sin(a))) + d = f"M{pts[0][0]:.0f},{pts[0][1]:.0f} " + " ".join( + f"Q{p[0]:.0f},{p[1]:.0f} {(p[0]+q[0])/2:.0f},{(p[1]+q[1])/2:.0f}" + for p, q in zip(pts, pts[1:] + pts[:1])) + "Z" + add(f'') + +def splats(cx, cy, spread, n, rmax, fill): + for _ in range(n): + x = cx + random.gauss(0, spread); y = cy + random.gauss(0, spread * .6) + add(f'') + +def drips(x0, x1, y, fill, n): + for _ in range(n): + x = random.uniform(x0, x1); l = random.uniform(15, 70); w = random.uniform(5, 11) + add(f'') + +def tent(x, y, w, h): + add(f'') + add(f'') + add(f'') + +def fig(x, y, s=1.0, pose="down", step=1): + L = lambda *p: " ".join(f"{a:.0f}" for a in p) + sw = 19 * s + st = f'stroke="{INK}" stroke-width="{sw:.0f}" stroke-linecap="round" stroke-linejoin="round" fill="none"' + hipy = y - 78 * s; shy = y - 150 * s + # piernas (andando) + add(f'') + add(f'') + # torso (chupa) + add(f'') + # cresta (detrás) + cabeza + if pose in ("fist", "cresta"): + for i in range(5): + dx = (i - 2) * 11 * s + add(f'') + add(f'') + # brazos + ls, rs = (x - 27 * s, shy + 8 * s), (x + 27 * s, shy + 8 * s) + if pose == "fist": + add(f'') + add(f'') + add(f'') + elif pose.startswith("pole"): + side = -1 if pose == "poleL" else 1 + px = x + side * 46 * s + add(f'') + add(f'') + else: + add(f'') + add(f'') + +add(f'') +add(f'') +# mancha roja de spray detrás +blob(600, 300, 250, 22, .18, RED) +splats(600, 300, 230, 70, 9, RED) +drips(420, 780, 470, RED, 9) +# calle: asfalto + paso de cebra +GY = 540 +add(f'') +for i in range(-2, 14): + x = i * 100 + add(f'') +# tiendas al fondo +tent(40, GY, 190, 140); tent(190, GY, 140, 100) +tent(930, GY, 200, 150); tent(1070, GY, 120, 90) +# pancarta entre dos palos +bx0, bx1, by = 300, 900, 50 +add(f'') +add(text_path("¡A LA CALLE!", 600, by + 86, 74, INK)) +drips(bx0 + 40, bx1 - 40, by + 122, INK, 6) +add(f'') +add(f'') +# gente +fig(bx0 + 46, GY - 4, 1.05, "poleL", 1) +fig(470, GY - 2, .95, "down", -1) +fig(600, GY, 1.15, "fist", 1) +fig(730, GY - 2, .95, "cresta", -1) +fig(bx1 - 46, GY - 4, 1.05, "poleR", 1) +# estrellas +for (sx, sy, r) in [(120, 90, 26), (1080, 110, 32), (990, 300, 16), (200, 300, 14)]: + import math + pts = " ".join(f"{sx + (r if i%2==0 else r*.42)*math.cos(math.pi/2 + i*math.pi/5):.0f},{sy - (r if i%2==0 else r*.42)*math.sin(math.pi/2 + i*math.pi/5):.0f}" for i in range(10)) + add(f'') +add('') +open(OUT, "w").write("\n".join(out))