#!/usr/bin/env python3 """Quattro proposte di icona per harbour-aira, alla misura vera (172 e 86 px). Si disegna a 344 px e si ridimensiona: bordi puliti a ogni taglia, come fa scripts/make_icons.py. Nessun logo di terzi (il permesso per nome e logo Aira non e' ancora arrivato), nessuna lettera generata. Uso: python3 scripts/icon_proposals.py Esito: docs/icona/proposte/*.png (172 e 86 px) + contatto.png con tutte e quattro """ import math import pathlib from PIL import Image, ImageDraw, ImageFont BASE = 344 # si disegna grande, si ridimensiona dopo OUT = pathlib.Path("docs/icona/proposte") TOP = (12, 62, 130) # blu profondo (identita' attuale) BOTTOM = (24, 148, 190) # azzurro WARM = (255, 138, 46) # arancione WHITE = (255, 255, 255, 240) BLUE = (14, 78, 148) # blu del fondo, per i ritagli (negative space) def rounded_mask(size, radius): m = Image.new("L", (size, size), 0) ImageDraw.Draw(m).rounded_rectangle([0, 0, size - 1, size - 1], radius=radius, fill=255) return m def gradient(size): img = Image.new("RGB", (1, size)) d = ImageDraw.Draw(img) for y in range(size): t = y / max(1, size - 1) d.point((0, y), tuple(int(TOP[i] + (BOTTOM[i] - TOP[i]) * t) for i in range(3))) return img.resize((size, size)) def onda(d, x0, x1, y, ampiezza, spessore, fill, creste=2): """Una linea ondulata: la stessa forma in tutti i disegni che la usano.""" punti = [] passi = 120 for i in range(passi + 1): t = i / passi x = x0 + (x1 - x0) * t yy = y + ampiezza * math.sin(2 * math.pi * creste * t) punti.append((x, yy)) d.line(punti, fill=fill, width=int(spessore), joint="curve") def base_icon(): img = gradient(BASE).convert("RGBA") return img, ImageDraw.Draw(img) def goccia(d, cx, cy, r, fill, fattore_punta=2.35): """Goccia con la punta smussata: la punta aguzza, a 86 px, sparisce.""" d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=fill) d.polygon([(cx - r * 0.11, cy - fattore_punta * r), (cx + r * 0.11, cy - fattore_punta * r), (cx + r * 0.99, cy + r * 0.10), (cx - r * 0.99, cy + r * 0.10)], fill=fill) # --------------------------------------------------------------------------- # # A. Casa con l'onda calda dentro: "la casa che viene scaldata" # --------------------------------------------------------------------------- # def casa_onda(): img, d = base_icon() tetto = [(BASE * 0.16, BASE * 0.50), (BASE * 0.50, BASE * 0.24), (BASE * 0.84, BASE * 0.50)] corpo = [BASE * 0.24, BASE * 0.50, BASE * 0.76, BASE * 0.78] d.polygon(tetto, fill=WHITE) d.rectangle(corpo, fill=WHITE) # onda dentro il corpo: non deve toccare il tetto (si fondeva a 86 px) onda(d, BASE * 0.26, BASE * 0.74, BASE * 0.665, BASE * 0.040, BASE * 0.078, WARM + (255,), creste=2) return img # --------------------------------------------------------------------------- # # B. Manopola: "il controllo" # --------------------------------------------------------------------------- # def manopola(): img, d = base_icon() m = BASE * 0.125 # margine piu' largo: il pomello non deve spessore = int(BASE * 0.115) # sfiorare l'angolo arrotondato riquadro = [m, m, BASE - m, BASE - m] # anello aperto in alto (la finestra della scala) e pomello in alto a destra d.arc(riquadro, start=295, end=245, fill=WHITE, width=spessore) cx = cy = BASE / 2 raggio = (BASE - 2 * m) / 2 ang = math.radians(315) px, py = cx + raggio * math.cos(ang), cy + raggio * math.sin(ang) r = BASE * 0.095 d.ellipse([px - r, py - r, px + r, py + r], fill=WARM + (255,)) # tacca al centro: il valore d.rounded_rectangle([cx - BASE * 0.032, cy - BASE * 0.14, cx + BASE * 0.032, cy + BASE * 0.14], radius=BASE * 0.032, fill=WHITE) return img # --------------------------------------------------------------------------- # # C. Goccia con la fiamma: "acqua calda" (evoluzione dell'icona attuale) # --------------------------------------------------------------------------- # def goccia_fiamma(): img, d = base_icon() cx, cy = BASE * 0.50, BASE * 0.615 r = BASE * 0.225 goccia(d, cx, cy, r, WHITE) img = img.transform(img.size, Image.AFFINE, (1, 0, 0, 0, 1, -BASE * 0.055)) d = ImageDraw.Draw(img) # fiamma piu' piccola: il bianco attorno deve restare spesso a 86 px cy2 = cy + r * 0.18 - BASE * 0.055 fr = r * 0.42 d.ellipse([cx - fr, cy2 - fr, cx + fr, cy2 + fr], fill=WARM + (255,)) d.polygon([(cx - fr * 0.13, cy2 - fr * 2.15), (cx + fr * 0.13, cy2 - fr * 2.15), (cx + fr * 0.96, cy2 + fr * 0.20)], fill=WARM + (255,)) return img # --------------------------------------------------------------------------- # # D. Casa e goccia insieme: "riscaldamento e acqua calda" # --------------------------------------------------------------------------- # def casa_goccia(): img, d = base_icon() tetto = [(BASE * 0.16, BASE * 0.50), (BASE * 0.50, BASE * 0.24), (BASE * 0.84, BASE * 0.50)] corpo = [BASE * 0.24, BASE * 0.50, BASE * 0.76, BASE * 0.78] d.polygon(tetto, fill=WHITE) d.rectangle(corpo, fill=WHITE) # goccia nel corpo della casa, in arancione: a 86 px il blu si perderebbe goccia(d, BASE * 0.50, BASE * 0.665, BASE * 0.092, WARM + (255,), fattore_punta=2.10) return img PROPOSTE = [ ("a-casa-onda", casa_onda, "A. Casa + onda calda"), ("b-manopola", manopola, "B. Manopola"), ("c-goccia-fiamma", goccia_fiamma, "C. Goccia + fiamma"), ("d-casa-goccia", casa_goccia, "D. Casa + goccia"), ] def salva(img, nome, size): fuori = img.resize((size, size), Image.LANCZOS) fuori = fuori.convert("RGB").convert("RGBA") fuori.putalpha(rounded_mask(size, int(size * 0.22))) OUT.mkdir(parents=True, exist_ok=True) percorso = OUT / f"{nome}-{size}.png" fuori.save(percorso) return percorso def contatto(): """Un'immagine sola: le quattro proposte a 172 px e, sotto, a 86 px.""" larghezza = 4 * 200 altezza = 200 + 130 + 30 tela = Image.new("RGB", (larghezza, altezza), (16, 20, 26)) d = ImageDraw.Draw(tela) try: piccolo = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14) except OSError: piccolo = ImageFont.load_default() for i, (nome, disegna, descrizione) in enumerate(PROPOSTE): img = disegna() grande = Image.open(salva(img, nome, 172)).convert("RGBA") piccola = Image.open(salva(img, nome, 86)).convert("RGBA") x = i * 200 + 14 tela.paste(grande, (x, 14), grande.split()[3]) tela.paste(piccola, (x + 43, 196), piccola.split()[3]) d.text((x, 296), descrizione, font=piccolo, fill=(200, 208, 216)) percorso = OUT / "contatto.png" tela.save(percorso) return percorso def main(): for nome, disegna, _ in PROPOSTE: img = disegna() for size in (172, 86): print("scritto", salva(img, nome, size)) print("scritto", contatto()) if __name__ == "__main__": main()