# Genera docs/calle.svg: una calle corta vista en diagonal. De cada portal sale # gente que avanza hacia la calzada (presencias abstractas, a la manera de Suso33). # Arriba no hay pisos: la fachada se desvanece, y la calle también hacia la derecha. import sys, random, math random.seed(26) OUT = sys.argv[1] W, H = 1600, 700 VX, VY, F, EYE = 1150, 210, 520, 1.6 # punto de fuga a la derecha, focal, ojo a 1,6 m FX, KX = -5.0, -2.6 # fachada y bordillo (m) INK, ORA = "#111", "#f04e23" o = [] add = o.append def P(X, Yh, Z): # Yh = altura sobre el suelo return VX + X * F / Z, VY + (EYE - Yh) * F / Z def pts(*ps): return " ".join(f"{x:.1f},{y:.1f}" for x, y in ps) def brush(a, b, w, n=4, col=INK, op=.9): for _ in range(n): j = lambda: random.uniform(-w, w) * .35 add(f'') HV = 570 # se recorta el suelo vacío de abajo add(f'') add(''' ''') ZN, ZF = 1.5, 70 # --- calle: suelo, bordillo y carretera; se va a blanco por la derecha --- add('') add('') add(f'') add(f'') # acera brush(P(KX, 0, ZN), P(KX, 0, 40), 9, 5) # bordillo brush(P(KX + .25, 0, ZN), P(KX + .25, 0, 34), 3, 2, col=ORA) # línea naranja de detalle add('') # --- fachada solo a la altura de los portales: arriba se desvanece --- add('') add(f'') brush(P(FX, 0, ZN), P(FX, 0, ZF), 7, 4) # pie de la fachada DOORS = [4.0, 7.5, 12.0, 18.5, 28.0] for zd in DOORS: d = [P(FX, 0, zd), P(FX, 2.5, zd), P(FX, 2.5, zd + 1.5), P(FX, 0, zd + 1.5)] add(f'') w = 90 / zd brush(d[0], d[1], w, 3); brush(d[1], d[2], w, 3) # marco if zd in (4.0, 12.0): # dintel en naranja brush(P(FX, 2.7, zd - .1), P(FX, 2.7, zd + 1.6), w * .5, 2, col=ORA, op=.8) add('') # --- presencias: salen de los portales hacia la calzada y hacia quien mira --- def presencia(X, Z, paso, brazo): fx, fy = P(X, 0, Z) h = 1.75 * F / Z # alto en pantalla lean = h * .09 # inclinados hacia delante (derecha) sx, sy = fx + lean, fy - h * .80 # hombros r = h * .095 w = h * .17 # sombra en el suelo, hacia atrás a la izquierda add(f'') if paso: # zancada: tronco y dos piernas de pincel hip = (fx + lean * .4, fy - h * .45) add(f'') for fxx, fyy in ((fx - h * .16, fy), (fx + h * .17, fy - h * .03)): add(f'') else: # cuerpo afilado add(f'') if brazo == "arriba": # brazo en alto hx, hy = sx + h * .14, sy - h * .30 add(f'') add(f'') elif brazo == "atras": # brazo que se balancea hacia atrás add(f'') add(f'') if random.random() < .15: # estela de movimiento en naranja for k in range(2): y = sy + h * (.15 + .22 * k) add(f'') gente = [] for zd in DOORS: n = random.randint(7, 10) if zd < 20 else 5 for i in range(n): t = (i + random.uniform(.1, .9)) / n # 0 en el portal, 1 en la calzada X = FX + .5 + t * random.uniform(4, 9) Z = zd + .75 - t * zd * random.uniform(.3, .5) if Z < 2.6 or any(abs(Z - g[0]) < .35 and abs(X - g[1]) < .45 for g in gente): continue paso = random.random() < .3 brazo = random.choices(["arriba", "atras", None], [.15, .2, .65])[0] gente.append((Z, X, paso, brazo)) gente.sort(reverse=True) # de lejos a cerca add('') for Z, X, paso, brazo in gente: presencia(X, Z, paso, brazo) add('') open(OUT, "w").write("\n".join(o)) print(f"{len(gente)} presencias", file=sys.stderr)