diff --git a/README.md b/README.md index 6f9b9f2..565aacf 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Take the Street](docs/marca/logotipo.svg) -![Gente que sale de los portales y avanza hacia la calle](docs/calle.svg) +![Una piña de gente de colores llena la calle](docs/calle.svg) **takethestreet.net / takethestreet.org**: red internacional de las acampadas por la vivienda que nacieron en la Puerta del Sol el 26 de septiembre de 2026 (26S). La vivienda es un derecho, no un negocio. @@ -137,38 +137,16 @@ Grindy Brush es una versión demo **solo para uso personal**. Antes de usar el l ## 6. Dibujo -![Gente que sale de los portales y avanza hacia la calle](docs/calle.svg) +![Una piña de gente de colores llena la calle](docs/calle.svg) -Una calle corta vista en diagonal, con el punto de fuga a la derecha. Solo se ve la planta baja: la fachada se desvanece hacia arriba, sin pisos, y la calle también se pierde por la derecha. De cada portal sale gente que avanza hacia la calzada y hacia quien mira. Son «presencias» abstractas a la manera de Suso33 (el mural *Presencias/Ausencias* de Tetuán): una mancha afilada con la cabeza, y algunas con zancada o con el puño en alto. Todo en negro; el naranja va solo en líneas de detalle (el bordillo, algún dintel, alguna estela). Se genera con `tools/calle.py`, sin dependencias: +Una calle vista desde un primer piso, con el punto de fuga a la derecha. La calle va solo con líneas negras a pincel: el pie de la fachada, los contornos de los portales, los bordillos y la línea discontinua del centro. No hay pisos arriba, y la acera de enfrente se pierde por la derecha. + +La gente va en color, en una piña que llena la calzada. Cada monigote es un solo trazo de spray en zigzag (una espiral para la cabeza, un cuello estrecho y un cuerpo alto que se afila hacia los pies), a la manera de las *Ausencias* y del contenedor de Suso33. Algunos dan una zancada o llevan el puño en alto. El corazón de la piña va en colores de fuego (blanco, amarillo, naranja y rojo) y alrededor fríos (morados, azules, verde y negro). La gente se reparte en metros sobre la calle, así que al fondo no se amontona. Se genera con `tools/calle.py`, sin dependencias: ``` python3 tools/calle.py docs/calle.svg ``` -### Bocetos nuevos (a la manera de Suso33) - -Cinco bocetos para sustituir el dibujo de la portada. Cada monigote es un solo trazo de spray en zigzag: una espiral para la cabeza y un cuerpo alto que se afila hacia los pies, como en las *Ausencias* y en el contenedor de Suso33. Van en piña: el centro en colores de fuego (blanco, amarillo, naranja, rojo) y alrededor fríos (morados, azules, blanco y negro). Se generan con `python3 tools/bocetos.py docs/bocetos`. - -1. **Contenedor**: la piña de fuego sobre una pared gris. - - ![Boceto 1](docs/bocetos/boceto-1.svg) - -2. **La piña sale a la calle**: la masa avanza hacia la derecha y se va soltando. - - ![Boceto 2](docs/bocetos/boceto-2.svg) - -3. **El portal**: del hueco sale una fila de gente y el fuego va delante. - - ![Boceto 3](docs/bocetos/boceto-3.svg) - -4. **Calle en diagonal**: la piña baja por la calle hacia quien mira. - - ![Boceto 4](docs/bocetos/boceto-4.svg) - -5. **Ausencias**: todos en negro y gris, y solo el corazón de la piña en fuego. - - ![Boceto 5](docs/bocetos/boceto-5.svg) - ## 7. Maqueta `mockup/index.html` es HTML y CSS, sin JavaScript. Se abre directamente en el navegador. Sigue el estilo de acampada26s.net (blanco y negro, bordes de 3 px, sombra de color): firma en la cabecera, portada con el lema y el dibujo debajo, y debajo el deck.Capturas al principio de este README. diff --git a/docs/bocetos/boceto-1.svg b/docs/bocetos/boceto-1.svg deleted file mode 100644 index e21d2c4..0000000 --- a/docs/bocetos/boceto-1.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/bocetos/boceto-2.svg b/docs/bocetos/boceto-2.svg deleted file mode 100644 index 62f073a..0000000 --- a/docs/bocetos/boceto-2.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/bocetos/boceto-3.svg b/docs/bocetos/boceto-3.svg deleted file mode 100644 index 9606fee..0000000 --- a/docs/bocetos/boceto-3.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/bocetos/boceto-4.svg b/docs/bocetos/boceto-4.svg deleted file mode 100644 index 6784535..0000000 --- a/docs/bocetos/boceto-4.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/bocetos/boceto-5.svg b/docs/bocetos/boceto-5.svg deleted file mode 100644 index 28585a7..0000000 --- a/docs/bocetos/boceto-5.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/calle.svg b/docs/calle.svg index 69679b9..e14b7cf 100644 --- a/docs/calle.svg +++ b/docs/calle.svg @@ -1,225 +1,405 @@ - + - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/deck-movil.png b/docs/deck-movil.png index 99d1bea..587f0a9 100644 Binary files a/docs/deck-movil.png and b/docs/deck-movil.png differ diff --git a/docs/deck.png b/docs/deck.png index 8475466..d0988ea 100644 Binary files a/docs/deck.png and b/docs/deck.png differ diff --git a/mockup/index.html b/mockup/index.html index 561659f..d305b8f 100644 --- a/mockup/index.html +++ b/mockup/index.html @@ -16,7 +16,7 @@

La vivienda es un derecho,
no un negocio

Red internacional de acampadas · 26S

- Gente que sale de los portales y avanza hacia la calle + Una piña de gente de colores llena la calle
#takethestreet#26s#acampadaporto#generazionesenzacasa#11N#viviendaya
diff --git a/tools/bocetos.py b/tools/bocetos.py deleted file mode 100644 index 7f7d387..0000000 --- a/tools/bocetos.py +++ /dev/null @@ -1,136 +0,0 @@ -# Genera docs/bocetos/boceto-1..5.svg. Monigotes de un solo trazo de spray en zigzag, -# altos y juntos, en piña: el centro en colores de fuego y alrededor fríos -# (a la manera del contenedor y las «Ausencias» de Suso33). -import sys, random, math, os -OUT = sys.argv[1] -W, H = 1600, 700 -FUEGO = ["#fffbe6", "#ffe066", "#ffb627", "#ff7b1c", "#f04e23", "#c8102e"] # del centro hacia fuera -FRIO = ["#5b2a86", "#8e44c9", "#3b5bdb", "#6c7ae0", "#e9e6f5", "#111", "#b07cd8"] - -DEFS = ''' - - - - -''' - -def fuego(d): # d = 0 en el centro de la piña, 1 en el borde - i = min(len(FUEGO) - 1, int(max(0, d + random.uniform(-.12, .12)) * len(FUEGO))) - return FUEGO[i] - -def monigote(o, x, y, h, col, lean=0.0, paso=False, brazo=False, sombra=False): - """Un trazo: cabeza en zigzag apretado y cuerpo en zigzag alto. (x, y) = pies.""" - sw = max(1.1, h * .011) - dy = sw * 2.0 # renglones del zigzag - if sombra: - o.append(f'') - top = y - h - pts = [] - yy = top - i = 0 - while yy <= y: - t = (yy - top) / h - cx = x + lean * h * (1 - t) - if t < .13: # cabeza - w = h * .05 * math.sin(math.pi * t / .13) ** .6 + sw * .3 - elif t < .16: # cuello - w = h * .008 - else: # cuerpo: hombros y se afila hacia los pies - u = (t - .16) / .84 - w = h * (.062 * (1 - u) ** .8 + .014) - if paso and u > .5: # zancada: el zigzag se abre en dos - k = (u - .5) / .5 - cx += (-1 if (i // 2) % 2 else 1) * h * .035 * k - w *= .6 - s = -1 if i % 2 else 1 - pts.append((cx + s * w + random.uniform(-.3, .3) * sw, yy)) - yy += dy * random.uniform(.85, 1.15) * (.55 if t < .13 else 1) - i += 1 - d = "M" + " L".join(f"{px:.1f},{py:.1f}" for px, py in pts) - o.append(f'') - if brazo: # brazo en alto - bx, by = x + lean * h * .84 + h * .05, top + h * .2 - p, n = [], 10 - for j in range(n): - t = j / (n - 1) - p.append((bx + t * h * .06 + (-1 if j % 2 else 1) * h * .012, by - t * h * .30)) - d = "M" + " L".join(f"{px:.1f},{py:.1f}" for px, py in p) - o.append(f'') - -def portal(o, x, y, w, h, fondo="#111"): - o.append(f'' - f'') - -def svg(cuerpo, fondo="#fff"): - return (f'{DEFS}' - f'' + "".join(cuerpo) + "\n") - -def fila(o, xs, y0, h0, centro, radio, frio=FRIO, lean=0.0, jit=12, hvar=.15, paso=.2, brazo=.08): - """Una piña: monigotes muy juntos; los cercanos al centro, en fuego.""" - for x in xs: - d = abs(x - centro) / radio - col = fuego(d) if d < 1 else random.choice(frio) - h = h0 * random.uniform(1 - hvar, 1 + hvar) * (1.05 if d < 1 else 1) - monigote(o, x, y0 + random.uniform(-jit, jit), h, col, lean, random.random() < paso, random.random() < brazo) - -# 1 · Contenedor: piña de fuego en una pared gris, como el contenedor -def b1(): - random.seed(1); o = [] - o.append('') - xs = sorted(random.uniform(90, 1510) for _ in range(70)) - fila(o, xs[::2], 610, 470, 800, 260) - fila(o, xs[1::2], 650, 500, 800, 230) - return o, "#9aa0a8" - -# 2 · Piña que sale a la calle: en blanco, la masa avanza hacia la derecha -def b2(): - random.seed(2); o = [] - o.append('') - xs = sorted(random.gauss(820, 300) for _ in range(55)) - xs = [x for x in xs if 80 < x < 1520] - fila(o, xs, 630, 400, 1000, 230, lean=.12, paso=.45, brazo=.12) - return o, "#fff" - -# 3 · El portal: del hueco sale la gente y el fuego va delante -def b3(): - random.seed(3); o = [] - o.append('') - portal(o, 150, 610, 190, 380) - o.append('') - for i in range(30): - t = i / 29 - x = 250 + t * 1250 + random.uniform(-25, 25) - d = abs(t - .32) / .25 - col = fuego(d) if d < 1 else random.choice(FRIO) - monigote(o, x, 610 + t * 70 + random.uniform(-8, 8), 300 + t * 120, col, .1, random.random() < .45, random.random() < .12) - return o, "#fff" - -# 4 · Calle en diagonal: la piña baja por la calle hacia quien mira -def b4(): - random.seed(4); o = [] - o.append('' - '') - ps = [] - for _ in range(1500): - t = random.random() - x = 1240 - t * 700 + random.gauss(0, 40 + t * 150) - y = 300 + t * 390 + random.gauss(0, 10) - if 290 < y < 695 and all(math.hypot(x - a, (y - b) * 3) > 22 + t * 50 for a, b, _ in ps): - ps.append((x, y, t)) - for x, y, t in sorted(ps, key=lambda p: p[1]): - d = math.hypot(x - 820, (y - 520) * 1.5) / 230 - col = fuego(d) if d < 1 else random.choice(FRIO) - monigote(o, x, y, 60 + t * 230, col, -.1, random.random() < .35, random.random() < .1) - return o, "#fff" - -# 5 · Ausencias: todos en negro y gris, y solo el corazón de la piña en fuego -def b5(): - random.seed(5); o = [] - xs = sorted(random.uniform(100, 1500) for _ in range(42)) - fila(o, xs, 650, 430, 900, 120, frio=["#111", "#111", "#333", "#555", "#888"], lean=.06, hvar=.12, paso=.3, brazo=.1) - return o, "#fff" - -os.makedirs(OUT, exist_ok=True) -for n, f in enumerate([b1, b2, b3, b4, b5], 1): - cuerpo, fondo = f() - open(f"{OUT}/boceto-{n}.svg", "w").write(svg(cuerpo, fondo)) diff --git a/tools/calle.py b/tools/calle.py index e66206c..fc6a1c7 100644 --- a/tools/calle.py +++ b/tools/calle.py @@ -1,111 +1,111 @@ -# 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. +# Genera docs/calle.svg: una calle en perspectiva dibujada solo con líneas negras +# (bordillos, pie de fachada, portales, línea del centro) y, encima, una piña de +# monigotes de un solo trazo de spray en zigzag, de colores: fuego en el centro y +# fríos alrededor (a la manera de Suso33). import sys, random, math -random.seed(26) +random.seed(4) 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" +W, H = 1600, 680 +VX, VY, F, EYE = 1180, 110, 520, 5.0 # punto de fuga, focal, ojo en un primer piso (m) +FACH, KI, KD = -7.0, -4.5, 3.6 # fachada, bordillo izquierdo y derecho (m) +INK = "#111" +FUEGO = ["#fffbe6", "#ffe066", "#ffb627", "#ff7b1c", "#f04e23", "#c8102e"] # del centro hacia fuera +FRIO = ["#5b2a86", "#8e44c9", "#3b5bdb", "#6c7ae0", "#b07cd8", "#2a9d8f", "#111"] o = [] add = o.append -def P(X, Yh, Z): # Yh = altura sobre el suelo +def P(X, Yh, Z): 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'') +def linea(a, b, w, op=.95): + add(f'') + +Y0 = 100 # arriba solo hay blanco: se recorta +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('') +# --- la calle: solo líneas negras a pincel --- +ZN, ZF = 3.0, 90 +add('') +linea(P(FACH, 0, ZN), P(FACH, 0, ZF), 7) # pie de la fachada +linea(P(KI, 0, ZN), P(KI, 0, ZF), 9) # bordillo +linea(P(KI + .18, 0, ZN), P(KI + .18, 0, ZF), 3, .7) # canto del bordillo +linea(P(KD, 0, 5), P(KD, 0, ZF), 6, .8) # bordillo de enfrente +z = 4.5 +while z < 60: # línea discontinua del centro + linea(P(-.3, 0, z), P(-.3, 0, z + 2.2), 2 + 16 / z, .8) + z += 5 +for zd in (4.0, 7.5, 11.5, 17.5, 26.0, 38.0): # portales: solo el contorno + a, b = P(FACH, 0, zd), P(FACH, 0, zd + 1.5) + c, d = P(FACH, 2.5, zd), P(FACH, 2.5, zd + 1.5) + w = 30 / zd + 2 + linea(a, c, w); linea(c, d, w); linea(d, b, w) + linea(P(FACH, 2.8, zd - .3), P(FACH, 2.8, zd + 1.8), w * .6, .6) # dintel +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'') +# --- monigotes de un trazo --- +def monigote(x, y, h, col, lean=0.0, paso=False, brazo=False): + sw = max(1.1, h * .011) + dy = sw * 2.0 + top = y - h + pts, yy, i = [], top, 0 + while yy <= y: + t = (yy - top) / h + cx = x + lean * h * (1 - t) + if t < .13: # cabeza + w = h * .05 * math.sin(math.pi * t / .13) ** .6 + sw * .3 + elif t < .16: # cuello + w = h * .008 + else: # cuerpo que se afila hacia los pies + u = (t - .16) / .84 + w = h * (.062 * (1 - u) ** .8 + .014) + if paso and u > .5: # zancada: el zigzag se abre en dos + cx += (-1 if (i // 2) % 2 else 1) * h * .035 * (u - .5) / .5 + w *= .6 + pts.append((cx + (-1 if i % 2 else 1) * w + random.uniform(-.3, .3) * sw, yy)) + yy += dy * random.uniform(.85, 1.15) * (.55 if t < .13 else 1) + i += 1 + d = "M" + " L".join(f"{px:.1f},{py:.1f}" for px, py in pts) + add(f'') + if brazo: # brazo en alto + bx, by = x + lean * h * .84 + h * .05, top + h * .2 + p = [(bx + j / 9 * h * .06 + (-1 if j % 2 else 1) * h * .012, by - j / 9 * h * .30) for j in range(10)] + d = "M" + " L".join(f"{px:.1f},{py:.1f}" for px, py in p) + add(f'') +# reparto en metros sobre el suelo: la piña por la calzada y regueros desde los portales +CX, CZ, R = -.4, 8.5, 2.8 # centro de la piña (fuego) y radio 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('') +def cabe(X, Z): + return all(math.hypot(X - a, (Z - b) * .8) > .5 for a, b in gente) +for _ in range(14000): + X, Z = random.gauss(-.2, 2.4), random.gauss(9.5, 4.2) + if KI - 1.5 < X < KD - .3 and 4.9 < Z < 32 and cabe(X, Z): + gente.append((X, Z)) +for zd in (7.5, 11.5, 17.5): # salen de los portales hacia la calzada + for k in range(4): + X, Z = FACH + .6 + k * .7, zd + .75 - k * .5 + if cabe(X, Z): + gente.append((X, Z)) + +add(f'') +for X, Z in sorted(gente, key=lambda g: -g[1]): # de lejos a cerca + x, y = P(X, 0, Z) + h = 1.7 * F / Z * random.uniform(.9, 1.08) + dist = math.hypot(X - CX, Z - CZ) / R + if dist < 1: + col = FUEGO[min(len(FUEGO) - 1, int(max(0, dist + random.uniform(-.12, .12)) * len(FUEGO)))] + else: + col = random.choice(FRIO) + monigote(x, y, h, col, random.uniform(-.06, .04), random.random() < .35, random.random() < .1) +add('') open(OUT, "w").write("\n".join(o)) -print(f"{len(gente)} presencias", file=sys.stderr) +print(f"{len(gente)} monigotes", file=sys.stderr)