Spaces:
Running
Running
Document visual QR signature generator
Browse files- scripts/generate_cards.py +39 -1
scripts/generate_cards.py
CHANGED
|
@@ -58,6 +58,43 @@ def planet_photo(card, draw, image_key, name, color, index):
|
|
| 58 |
card.paste(source, (370, 390), mask)
|
| 59 |
draw.ellipse((370, 390, 830, 850), outline=color, width=7)
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
items = json.loads((ROOT / "data" / "objects.json").read_text(encoding="utf-8"))
|
| 62 |
items.extend([{"name":"M谩s informaci贸n","qr_id":"MAS_001"},{"name":"Otro planeta","qr_id":"OTRA_001"}])
|
| 63 |
for item in items:
|
|
@@ -79,6 +116,7 @@ for item in items:
|
|
| 79 |
centered(draw,"MUESTRA ESTA TARJETA A REACHY" if qr_id not in {"MAS_001","OTRA_001"} else "RESPUESTA PARA REACHY",997,font(24,True),"#bcd0ff")
|
| 80 |
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=12, border=4); qr.add_data(qr_id); qr.make(fit=True)
|
| 81 |
qr_image = qr.make_image(fill_color="#071227",back_color="white").convert("RGBA"); qr_image.thumbnail((490,490),Image.Resampling.LANCZOS)
|
|
|
|
| 82 |
draw.rounded_rectangle((300,1085,900,1595),34,fill="white",outline="#cadbff",width=7); card.alpha_composite(qr_image,((SIZE[0]-qr_image.width)//2,1100))
|
| 83 |
-
|
| 84 |
print(f"{len(items)} tarjetas Astro Reachy creadas en {OUT}")
|
|
|
|
| 58 |
card.paste(source, (370, 390), mask)
|
| 59 |
draw.ellipse((370, 390, 830, 850), outline=color, width=7)
|
| 60 |
|
| 61 |
+
def qr_signature(draw, name, qr_id, color, index):
|
| 62 |
+
"""A帽ade una firma humana, sin invadir la zona silenciosa del QR.
|
| 63 |
+
|
| 64 |
+
El QR mantiene siempre tinta oscura sobre blanco: los patrones se sit煤an
|
| 65 |
+
alrededor de su tarjeta y sirven para reconocerla de un vistazo y orientar
|
| 66 |
+
a quien la muestra a la c谩mara.
|
| 67 |
+
"""
|
| 68 |
+
# Banda identificativa y marco crom谩tico propios de cada astro.
|
| 69 |
+
draw.rounded_rectangle((300, 1038, 900, 1078), 18, fill=color)
|
| 70 |
+
centered(draw, f"FIRMA VISUAL 路 {name.upper()}", 1047, font(17, True), "#071227")
|
| 71 |
+
draw.rounded_rectangle((288, 1073, 912, 1607), 42, outline=color, width=8)
|
| 72 |
+
|
| 73 |
+
# Cuatro familias de patrones, escogidas de forma estable por planeta.
|
| 74 |
+
# Est谩n fuera del propio QR y de sus m谩rgenes blancos obligatorios.
|
| 75 |
+
style = index % 4
|
| 76 |
+
if style == 0: # 贸rbitas: nodos redondos
|
| 77 |
+
for x, y in ((275, 1125), (925, 1190), (275, 1490), (925, 1550)):
|
| 78 |
+
draw.ellipse((x-14, y-14, x+14, y+14), fill=color, outline="#f4f8ff", width=3)
|
| 79 |
+
elif style == 1: # cometas: peque帽as estelas diagonales
|
| 80 |
+
for x, y, direction in ((270, 1140, 1), (930, 1215, -1), (270, 1525, 1), (930, 1450, -1)):
|
| 81 |
+
draw.line((x, y, x + 32 * direction, y - 22), fill=color, width=9)
|
| 82 |
+
draw.ellipse((x-7, y-7, x+7, y+7), fill="#f4f8ff", outline=color, width=3)
|
| 83 |
+
elif style == 2: # constelaci贸n: cuadrados y l铆neas discontinuas
|
| 84 |
+
for x, y in ((274, 1130), (925, 1130), (274, 1535), (925, 1535)):
|
| 85 |
+
draw.rounded_rectangle((x-13, y-13, x+13, y+13), 5, fill=color, outline="#f4f8ff", width=3)
|
| 86 |
+
for y in (1220, 1415):
|
| 87 |
+
draw.line((252, y, 282, y), fill=color, width=6)
|
| 88 |
+
draw.line((918, y, 948, y), fill=color, width=6)
|
| 89 |
+
else: # asteroides: tri谩ngulos con orientaci贸n alterna
|
| 90 |
+
for x, y, up in ((274, 1140, True), (926, 1190, False), (274, 1510, False), (926, 1550, True)):
|
| 91 |
+
points = [(x, y-16), (x-15, y+14), (x+15, y+14)] if up else [(x, y+16), (x-15, y-14), (x+15, y-14)]
|
| 92 |
+
draw.polygon(points, fill=color, outline="#f4f8ff")
|
| 93 |
+
|
| 94 |
+
# Un identificador textual visible, 煤til si las tarjetas se mezclan.
|
| 95 |
+
draw.rounded_rectangle((405, 1605, 795, 1654), 20, fill="#071227", outline=color, width=3)
|
| 96 |
+
centered(draw, f"{qr_id} 路 PATR脫N {style + 1}", 1618, font(17, True), "#f4f8ff")
|
| 97 |
+
|
| 98 |
items = json.loads((ROOT / "data" / "objects.json").read_text(encoding="utf-8"))
|
| 99 |
items.extend([{"name":"M谩s informaci贸n","qr_id":"MAS_001"},{"name":"Otro planeta","qr_id":"OTRA_001"}])
|
| 100 |
for item in items:
|
|
|
|
| 116 |
centered(draw,"MUESTRA ESTA TARJETA A REACHY" if qr_id not in {"MAS_001","OTRA_001"} else "RESPUESTA PARA REACHY",997,font(24,True),"#bcd0ff")
|
| 117 |
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=12, border=4); qr.add_data(qr_id); qr.make(fit=True)
|
| 118 |
qr_image = qr.make_image(fill_color="#071227",back_color="white").convert("RGBA"); qr_image.thumbnail((490,490),Image.Resampling.LANCZOS)
|
| 119 |
+
qr_signature(draw, name, qr_id, PALETTES[name], items.index(item))
|
| 120 |
draw.rounded_rectangle((300,1085,900,1595),34,fill="white",outline="#cadbff",width=7); card.alpha_composite(qr_image,((SIZE[0]-qr_image.width)//2,1100))
|
| 121 |
+
card.convert("RGB").save(OUT/f"{qr_id}.png",dpi=(300,300))
|
| 122 |
print(f"{len(items)} tarjetas Astro Reachy creadas en {OUT}")
|