BravoRobots commited on
Commit
a2cdb29
路
verified 路
1 Parent(s): 50d781e

Document visual QR signature generator

Browse files
Files changed (1) hide show
  1. 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
- centered(draw,qr_id,1615,font(27,True),"#d8e5ff"); card.convert("RGB").save(OUT/f"{qr_id}.png",dpi=(300,300))
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}")