Cinco ejes y un faro: Hogar, Emergencia climática, Rompiendo fronteras, Pueblos libres e Igualdad
- Techo pasa a Hogar, Tierra a Emergencia climática, Sin fronteras a Rompiendo fronteras y Paz a Pueblos libres («No a la guerra»), en las dos webs y los cinco idiomas. - Igualdad deja de ser la franja de los feminismos y es una tarjeta más. - Iconos nuevos pintados con spray: casa, planeta que gotea, valla abierta, cadena rota y signo igual (tools/ejes.py). - El símbolo deja la farola por un faro con la luz amarilla: haz horizontal y ondas en círculo (tools/faro.py). Cambia en la cabecera, el favicon y el mapa. Fuera tools/farola.py y sus propuestas. - Ideario y README al día. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a1700098b3
commit
879764b6a9
56 changed files with 492 additions and 399 deletions
BIN
tools/__pycache__/faro.cpython-312.pyc
Normal file
BIN
tools/__pycache__/faro.cpython-312.pyc
Normal file
Binary file not shown.
103
tools/ejes.py
103
tools/ejes.py
|
|
@ -1,8 +1,8 @@
|
|||
# Genera sites/<web>/img/eje-*.svg: una piña pequeña de tres monigotes de un trazo en zigzag,
|
||||
# en el color de cada eje del ideario (los mismos colores que los regueros del dibujo).
|
||||
# Genera sites/<web>/img/eje-*.svg: un símbolo por eje pintado con spray (relleno en zigzag,
|
||||
# trazos con pulso y chorreones), en el color de cada eje (los mismos que los regueros del dibujo).
|
||||
import sys, random, math, os
|
||||
OUT = sys.argv[1]
|
||||
EJES = {"techo": "#f04e23", "tierra": "#14965a", "fronteras": "#f2b705", "paz": "#3b5bdb", "feminismos": "#8e44c9"}
|
||||
EJES = {"hogar": "#f04e23", "clima": "#14965a", "fronteras": "#f2b705", "pueblos": "#3b5bdb", "igualdad": "#8e44c9"}
|
||||
DEFS = ('<defs><filter id="spray" x="-30%" y="-10%" width="160%" height="120%"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="2" seed="3" result="t"/>'
|
||||
'<feDisplacementMap in="SourceGraphic" in2="t" scale="1.6" result="d"/><feGaussianBlur in="d" stdDeviation=".4" result="b"/>'
|
||||
'<feGaussianBlur in="SourceGraphic" stdDeviation="2.5" result="n"/><feComponentTransfer in="n" result="n2"><feFuncA type="linear" slope=".3"/></feComponentTransfer>'
|
||||
|
|
@ -34,11 +34,102 @@ def monigote(o, x, y, h, col, brazo=False, paso=False):
|
|||
p = [(bx + j / 9 * h * .06 + (-1 if j % 2 else 1) * h * .014, by - j / 9 * h * .3) for j in range(10)]
|
||||
o.append(f'<path d="M' + " L".join(f"{px:.1f},{py:.1f}" for px, py in p) + f'" fill="none" stroke="{col}" stroke-width="{sw:.1f}" stroke-linecap="round" filter="url(#spray)"/>')
|
||||
|
||||
def j(v, a):
|
||||
return v + random.uniform(-a, a)
|
||||
|
||||
def relleno(o, poly, col, paso=2.6, sw=3.6):
|
||||
# Zigzag horizontal que rellena un polígono convexo, como una pasada de spray.
|
||||
ys = [y for _, y in poly]
|
||||
pts, y, i = [], min(ys) + sw * .4, 0
|
||||
while y <= max(ys) - sw * .4:
|
||||
xs = []
|
||||
for (x1, y1), (x2, y2) in zip(poly, poly[1:] + poly[:1]):
|
||||
if (y1 <= y < y2) or (y2 <= y < y1):
|
||||
xs.append(x1 + (y - y1) * (x2 - x1) / (y2 - y1))
|
||||
if len(xs) >= 2:
|
||||
a, b = min(xs), max(xs)
|
||||
pts.append((j(a if i % 2 else b, 1.2), y))
|
||||
i += 1
|
||||
y += paso * random.uniform(.85, 1.15)
|
||||
o.append(f'<path d="M{" L".join(f"{x:.1f},{y:.1f}" for x, y in pts)}" fill="none" stroke="{col}" stroke-width="{sw:.1f}" stroke-linejoin="round" stroke-linecap="round" filter="url(#spray)"/>')
|
||||
|
||||
def linea(o, pts, col, sw=5):
|
||||
# Trazo con pulso: dos pasadas ligeramente desplazadas.
|
||||
for k in range(2):
|
||||
d = "M" + " L".join(f"{j(x, 1.1):.1f},{j(y, 1.1):.1f}" for x, y in pts)
|
||||
o.append(f'<path d="{d}" fill="none" stroke="{col}" stroke-width="{sw * (1 if k == 0 else .6):.1f}" stroke-linejoin="round" stroke-linecap="round" opacity="{1 if k == 0 else .7}" filter="url(#spray)"/>')
|
||||
|
||||
def chorreon(o, x, y, largo, col):
|
||||
y -= 6
|
||||
o.append(f'<path d="M{x:.1f},{y:.1f} L{j(x, .6):.1f},{y + largo:.1f}" stroke="{col}" stroke-width="2.4" stroke-linecap="round" filter="url(#spray)"/>')
|
||||
o.append(f'<circle cx="{x:.1f}" cy="{y + largo:.1f}" r="2.2" fill="{col}" filter="url(#spray)"/>')
|
||||
|
||||
def hogar(o, col):
|
||||
# Una casa: tejado, dos muros y el dintel; la puerta queda en blanco.
|
||||
relleno(o, [(70, 22), (128, 72), (12, 72)], col)
|
||||
relleno(o, [(24, 72), (54, 72), (54, 150), (24, 150)], col)
|
||||
relleno(o, [(86, 72), (116, 72), (116, 150), (86, 150)], col)
|
||||
relleno(o, [(54, 72), (86, 72), (86, 96), (54, 96)], col)
|
||||
linea(o, [(98, 50), (98, 26), (110, 26), (110, 60)], col, 5)
|
||||
for x, l in ((30, 22), (47, 14), (92, 28), (110, 16)):
|
||||
chorreon(o, x, 150, l, col)
|
||||
|
||||
def clima(o, col):
|
||||
# El planeta que se derrite: anillo en zigzag, meridianos y chorreones.
|
||||
cx, cy, r, pts = 70, 72, 52, []
|
||||
for i in range(220):
|
||||
a = 2 * math.pi * i / 219
|
||||
rr = r - (0 if i % 2 else 11) + random.uniform(-1.5, 1.5)
|
||||
pts.append((cx + rr * math.cos(a), cy + rr * math.sin(a)))
|
||||
o.append(f'<path d="M{" L".join(f"{x:.1f},{y:.1f}" for x, y in pts)}" fill="none" stroke="{col}" stroke-width="3.2" stroke-linejoin="round" filter="url(#spray)"/>')
|
||||
linea(o, [(cx + 44 * math.cos(math.pi * k / 20), cy + 44 * math.sin(math.pi * k / 20) * .32) for k in range(21)], col, 4)
|
||||
linea(o, [(cx + 44 * math.cos(math.pi * k / 20) * .38, cy - 44 * math.sin(math.pi * k / 20)) for k in range(-10, 11)], col, 4)
|
||||
for x, l in ((40, 26), (55, 40), (72, 30), (88, 44), (102, 20)):
|
||||
y0 = cy + math.sqrt(max(0, r * r - (x - cx) ** 2)) - 4
|
||||
chorreon(o, x, y0, l, col)
|
||||
|
||||
def fronteras(o, col):
|
||||
# Una valla rota: barrotes que se abren en el centro y alambre de espino cortado.
|
||||
for x in (14, 32, 108, 126):
|
||||
linea(o, [(x, 40), (x, 150)], col, 6)
|
||||
linea(o, [(50, 150), (52, 100), (40, 56)], col, 6)
|
||||
linea(o, [(90, 150), (88, 100), (100, 56)], col, 6)
|
||||
linea(o, [(6, 62), (44, 66)], col, 5)
|
||||
linea(o, [(96, 66), (134, 62)], col, 5)
|
||||
linea(o, [(6, 112), (54, 114)], col, 5)
|
||||
linea(o, [(86, 114), (134, 112)], col, 5)
|
||||
for x, y in ((22, 64), (116, 64), (22, 113), (118, 113)):
|
||||
linea(o, [(x - 4, y - 4), (x + 4, y + 4)], col, 4)
|
||||
linea(o, [(x + 4, y - 4), (x - 4, y + 4)], col, 4)
|
||||
|
||||
def igualdad(o, col):
|
||||
# El signo igual, en dos barras de spray con chorreones.
|
||||
relleno(o, [(14, 50), (126, 46), (126, 76), (14, 80)], col)
|
||||
relleno(o, [(14, 100), (126, 96), (126, 126), (14, 130)], col)
|
||||
for x, y, l in ((30, 79, 14), (100, 75, 10), (46, 129, 22), (112, 126, 14)):
|
||||
chorreon(o, x, y, l, col)
|
||||
|
||||
def eslabon(o, cx, cy, ang, col, roto=False):
|
||||
# Un eslabón: óvalo a pulso; el roto se abre por un extremo.
|
||||
ca, sa, pts = math.cos(ang), math.sin(ang), []
|
||||
for k in range(29 if roto else 37):
|
||||
t = 2 * math.pi * (k / 36 + (.12 if roto else 0))
|
||||
x, y = 19 * math.cos(t), 11 * math.sin(t)
|
||||
pts.append((cx + x * ca - y * sa, cy + x * sa + y * ca))
|
||||
linea(o, pts, col, 7)
|
||||
|
||||
def pueblos(o, col):
|
||||
# Una cadena rota: dos tramos enlazados que se abren en el centro, con chispas en el corte.
|
||||
eslabon(o, 20, 118, -.3, col)
|
||||
eslabon(o, 48, 108, -.3, col, roto=True)
|
||||
eslabon(o, 92, 108, .3 + math.pi, col, roto=True)
|
||||
eslabon(o, 120, 118, .3, col)
|
||||
for a in (-2.5, -2.0, -1.57, -1.14, -.64):
|
||||
linea(o, [(70 + 16 * math.cos(a), 96 + 16 * math.sin(a)), (70 + 36 * math.cos(a), 96 + 36 * math.sin(a))], col, 5)
|
||||
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
for n, (nombre, col) in enumerate(EJES.items()):
|
||||
random.seed(40 + n)
|
||||
o = []
|
||||
monigote(o, 38, 150, 112, col, paso=True)
|
||||
monigote(o, 102, 156, 120, col, paso=True)
|
||||
monigote(o, 70, 160, 140, col, brazo=True)
|
||||
globals()[nombre](o, col)
|
||||
open(f"{OUT}/eje-{nombre}.svg", "w").write(f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 170" width="140" height="170">{DEFS}' + "".join(o) + "</svg>\n")
|
||||
|
|
|
|||
50
tools/faro.py
Normal file
50
tools/faro.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Genera el símbolo: un faro dentro de un aro grueso, como un emblema de tienda de graffiti.
|
||||
# La luz es amarilla: un haz horizontal que se abre hacia los dos lados y un halo en círculo.
|
||||
import sys
|
||||
INK, BL, NAR = "#111", "#fff", "#f04e23"
|
||||
LUZ, CLARA = "#ffc21a", "#fff4b8"
|
||||
CX, CY = 50, 36 # centro de la linterna
|
||||
|
||||
def luz_(luz, clara):
|
||||
# Haz: dos cuñas horizontales que se ensanchan hacia fuera, y ondas en círculo alrededor.
|
||||
haz = (f'<path d="M{CX},{CY - 3} L0,{CY - 17} V{CY + 17} L{CX},{CY + 3} L100,{CY + 17} V{CY - 17} Z" fill="{luz}" opacity=".9"/>')
|
||||
ondas = "".join(f'<circle cx="{CX}" cy="{CY}" r="{r}" fill="none" stroke="{luz}" stroke-width="{w}" opacity="{o}"/>'
|
||||
for r, w, o in ((19, 3, .9), (27, 2.2, .55)))
|
||||
halo = f'<circle cx="{CX}" cy="{CY}" r="12" fill="{luz}"/><circle cx="{CX}" cy="{CY}" r="6.5" fill="{clara}"/>'
|
||||
return haz + ondas + halo
|
||||
|
||||
def torre(ink, papel):
|
||||
# Torre que se estrecha hacia arriba, con dos franjas, la linterna y el tejado en punta.
|
||||
cuerpo = f'<path d="M42,46 H58 L65,100 H35 Z" fill="{ink}"/>'
|
||||
franjas = "".join(f'<path d="M{43.3 - (y - 46) * 7 / 54:.1f},{y} H{56.7 + (y - 46) * 7 / 54:.1f} L{57.3 + (y + 6 - 46) * 7 / 54:.1f},{y + 6} H{42.7 - (y + 6 - 46) * 7 / 54:.1f} Z" fill="{papel}"/>'
|
||||
for y in (56, 72))
|
||||
balcon = f'<rect x="39" y="43" width="22" height="4" rx="1" fill="{ink}"/>'
|
||||
linterna = f'<path d="M44,43 V31 M56,43 V31" stroke="{ink}" stroke-width="2.6"/>'
|
||||
tejado = f'<path d="M41,31 L50,21 L59,31 Z" fill="{ink}"/>'
|
||||
return cuerpo + franjas + balcon + linterna + tejado
|
||||
|
||||
def faro(estilo="aro", ink=INK, luz=LUZ, papel=BL, clara=CLARA, clip="c", grueso=7, fondo_aro=None):
|
||||
if estilo == "aro":
|
||||
defs = f'<clipPath id="{clip}"><circle cx="50" cy="50" r="42"/></clipPath>'
|
||||
fondo = f'<circle cx="50" cy="50" r="47" fill="{fondo_aro}"/>' if fondo_aro else ""
|
||||
dentro = f'<g clip-path="url(#{clip})">{luz_(luz, clara)}{torre(ink, papel)}</g>'
|
||||
aro = f'<circle cx="50" cy="50" r="45" fill="none" stroke="{ink}" stroke-width="{grueso}"/>'
|
||||
return defs + fondo + dentro + aro
|
||||
# Pegatina: círculo amarillo, la luz en blanco
|
||||
defs = f'<clipPath id="{clip}"><circle cx="50" cy="50" r="48"/></clipPath>'
|
||||
return defs + f'<circle cx="50" cy="50" r="48" fill="{luz}"/><g clip-path="url(#{clip})">{luz_(papel, BL)}{torre(ink, luz)}</g>'
|
||||
|
||||
def svg(cuerpo, fondo=None):
|
||||
bg = f'<rect width="100" height="100" fill="{fondo}"/>' if fondo else ""
|
||||
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">{bg}{cuerpo}</svg>\n'
|
||||
|
||||
def simbolo_mapa():
|
||||
# El mismo faro para el <symbol id="faro"> del mapa estático: fondo blanco y aro más grueso.
|
||||
return faro(clip="fc", grueso=9, fondo_aro=BL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
d = sys.argv[1]
|
||||
open(f"{d}/simbolo.svg", "w").write(svg(faro()))
|
||||
open(f"{d}/simbolo-negativo.svg", "w").write(svg(faro(ink=BL, papel=INK), INK))
|
||||
open(f"{d}/simbolo-naranja.svg", "w").write(svg(faro(luz=BL, papel=NAR, clara="#ffe2a8"), NAR))
|
||||
open(f"{d}/simbolo-pegatina.svg", "w").write(svg(faro("pegatina")))
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import sys
|
||||
NAR, LUZ, CLARA, INK, BL = "#f04e23", "#f45a1c", "#ffb03a", "#111", "#fff"
|
||||
|
||||
# La luz baja de la cabeza, llega al suelo y se abre en un charco (naranja por fuera, claro en el centro).
|
||||
CONOS = {
|
||||
"recta": "M57,33 H77 L98,92 H36 Z",
|
||||
"onda-suave": "M57,33 H77 C82,46 85,55 85,62 C85,69 88,73 89,76 H41 C42,72 45,69 45,62 C45,54 51,45 57,33 Z",
|
||||
"campana": "M57,33 H77 C78,52 82,66 92,76 H38 C48,66 56,52 57,33 Z",
|
||||
"onda": "M57,33 H77 C86,42 80,52 86,61 C90,67 91,72 90,76 H40 C39,72 41,67 44,61 C48,52 44,42 57,33 Z",
|
||||
# S pronunciada: los dos lados ondean hacia el mismo lado, como una bandera
|
||||
"s": "M57,33 H77 C64,48 102,60 92,76 H40 C58,60 40,48 57,33 Z",
|
||||
"s-media": "M57,33 H77 C70,48 96,60 92,76 H40 C54,60 46,48 57,33 Z",
|
||||
# S en espejo: se estrecha arriba y se abre abajo como una llama
|
||||
"s-espejo": "M57,33 H77 C66,50 100,58 92,76 H38 C30,58 68,50 57,33 Z",
|
||||
"s-espejo-media": "M57,33 H77 C70,50 94,58 92,76 H38 C36,58 64,50 57,33 Z",
|
||||
}
|
||||
CHARCO = {"onda-suave": 24, "campana": 27, "onda": 25, "s": 26, "s-media": 26, "s-espejo": 27, "s-espejo-media": 27}
|
||||
|
||||
def luz_(cono, luz, clara, ink, suelo=False):
|
||||
out = f'<path d="{CONOS[cono]}" fill="{luz}"/>'
|
||||
if cono in CHARCO:
|
||||
rx, ry = CHARCO[cono], 5.5
|
||||
out += (f'<ellipse cx="65" cy="76" rx="{rx}" ry="{ry}" fill="{luz}"/>'
|
||||
f'<ellipse cx="65" cy="76" rx="{rx * .62:.1f}" ry="{ry * .55:.1f}" fill="{clara}"/>')
|
||||
if cono == "recta":
|
||||
out += f'<ellipse cx="67" cy="86" rx="22" ry="5" fill="{ink}" opacity=".25"/>'
|
||||
if suelo:
|
||||
out += f'<path d="M8,84 H92" stroke="{ink}" stroke-width="3"/>'
|
||||
return out
|
||||
|
||||
def farola(estilo="aro", ink=INK, luz=LUZ, fondo=None, papel=BL, clara=CLARA, cono="s", suelo=False):
|
||||
bg = f'<rect width="100" height="100" fill="{fondo}"/>' if fondo else ""
|
||||
if estilo == "aro":
|
||||
defs = '<defs><clipPath id="c"><circle cx="50" cy="50" r="42"/></clipPath></defs>'
|
||||
cono = f'<g clip-path="url(#c)">{luz_(cono, luz, clara, ink, suelo)}</g>'
|
||||
poste = f'<path d="M37,96 V40 Q37,25 52,25 H60" fill="none" stroke="{ink}" stroke-width="8"/>'
|
||||
cabeza = f'<rect x="55" y="21" width="27" height="11" rx="3" fill="{ink}"/>'
|
||||
aro = f'<circle cx="50" cy="50" r="45" fill="none" stroke="{ink}" stroke-width="7"/>'
|
||||
return defs + cono + f'<g clip-path="url(#c)">{poste}</g>' + cabeza + aro
|
||||
pegatina = f'<circle cx="50" cy="50" r="48" fill="{luz}"/>'
|
||||
poste = f'<path d="M33,99 V38 Q33,22 49,22 H58" fill="none" stroke="{ink}" stroke-width="9"/>'
|
||||
cabeza = f'<rect x="52" y="17" width="30" height="11" rx="3" fill="{ink}"/>'
|
||||
rayos = "".join(f'<path d="M{x0},{y0} L{x1},{y1}" stroke="{papel}" stroke-width="4" stroke-linecap="round"/>'
|
||||
for x0, y0, x1, y1 in ((58, 35, 50, 52), (67, 36, 67, 56), (76, 35, 84, 52)))
|
||||
return f'<defs><clipPath id="c"><circle cx="50" cy="50" r="48"/></clipPath></defs>{pegatina}<g clip-path="url(#c)">{poste}</g>{cabeza}{rayos}'
|
||||
|
||||
def svg(cuerpo, fondo=None):
|
||||
bg = f'<rect width="100" height="100" fill="{fondo}"/>' if fondo else ""
|
||||
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">{bg}{cuerpo}</svg>\n'
|
||||
|
||||
if __name__ == "__main__":
|
||||
d = sys.argv[1]
|
||||
open(f"{d}/simbolo.svg", "w").write(svg(farola()))
|
||||
open(f"{d}/simbolo-negativo.svg", "w").write(svg(farola(ink=BL, papel=INK), INK))
|
||||
open(f"{d}/simbolo-naranja.svg", "w").write(svg(farola(luz=BL, papel=NAR, clara="#ffe2a8"), NAR))
|
||||
open(f"{d}/simbolo-pegatina.svg", "w").write(svg(farola("pegatina")))
|
||||
# Propuestas que no se usan, para tenerlas a mano
|
||||
import os
|
||||
os.makedirs(f"{d}/propuestas", exist_ok=True)
|
||||
for n, (cono, suelo) in enumerate((("onda-suave", False), ("campana", False), ("onda", False), ("campana", True)), 1):
|
||||
open(f"{d}/propuestas/farola-{n}-{cono}{'-suelo' if suelo else ''}.svg", "w").write(svg(farola(cono=cono, suelo=suelo)))
|
||||
open(f"{d}/propuestas/farola-recta.svg", "w").write(svg(farola(cono="recta")))
|
||||
for n, cono in enumerate(("s", "s-media", "s-espejo", "s-espejo-media"), 5):
|
||||
open(f"{d}/propuestas/farola-{n}-{cono}.svg", "w").write(svg(farola(cono=cono)))
|
||||
Loading…
Add table
Add a link
Reference in a new issue