#!/usr/bin/env python3
"""
Il set di segni dell'interfaccia: undici icone disegnate qui, non prese da un
catalogo.

Perche' non un font di icone. MaterialCommunityIcons e SF Symbols sono ottimi e
si riconoscono a colpo d'occhio — ed e' esattamente il problema: si riconoscono
come le icone di qualcun altro. Un'app che vuole sembrare disegnata apposta non
puo' avere i segni che ha chiunque. Sono undici, si disegnano.

Perche' immagini e non vettori. react-native-svg e' una dipendenza nativa, e
questo progetto non e' ancora mai stato compilato sul serio: si aggiungono
dipendenze native quando c'e' un motivo, non per undici disegni. Le PNG si
colorano con `tintColor`, quindi seguono il tema come farebbe un vettore.

LA REGOLA DEL SET, che e' quello che rende undici disegni un insieme:
  - griglia di 32, tutto costruito dentro;
  - tratto di 2,6 — piu' spesso della media dei set di sistema, perche' questa
    e' un'app che si guarda da mezzo metro con le mani sporche;
  - terminazioni arrotondate, sempre;
  - le forme chiuse hanno lo stesso raggio d'angolo, 5 su 32, che e' la stessa
    proporzione della pastiglia delle categorie (16 su 52);
  - niente riempimenti: solo tratto, cosi' i segni pesano tutti uguale.

Si disegna a 4x e si riduce: e' l'antialiasing, e senza i tratti obliqui
verrebbero seghettati.

Si rilancia con: python3 scripts/genera-segni.py
"""
import math
from pathlib import Path

from PIL import Image, ImageDraw

G = 32               # la griglia
SCALA = 12           # 32 * 12 = 384, ridotto poi a 96 (tre volte 32)
LATO = 96
TRATTO = 2.6
RAGGIO = 5

USCITA = Path(__file__).resolve().parent.parent / 'assets' / 'segni'


def tela():
    return Image.new('RGBA', (G * SCALA, G * SCALA), (0, 0, 0, 0))


def p(*coppie):
    """Da coordinate della griglia a pixel."""
    return [(x * SCALA, y * SCALA) for x, y in coppie]


def linea(d, *punti, chiusa=False):
    pt = p(*punti)
    if chiusa:
        pt = pt + [pt[0]]
    d.line(pt, fill=(255, 255, 255, 255), width=round(TRATTO * SCALA), joint='curve')
    # I capi: PIL non arrotonda le estremita', si aggiungono a mano.
    r = TRATTO * SCALA / 2
    for x, y in (pt[0], pt[-1]):
        d.ellipse([x - r, y - r, x + r, y + r], fill=(255, 255, 255, 255))


def cerchio(d, cx, cy, raggio):
    r = raggio * SCALA
    x, y = cx * SCALA, cy * SCALA
    d.ellipse([x - r, y - r, x + r, y + r], outline=(255, 255, 255, 255),
              width=round(TRATTO * SCALA))


def disco(d, cx, cy, raggio):
    r = raggio * SCALA
    x, y = cx * SCALA, cy * SCALA
    d.ellipse([x - r, y - r, x + r, y + r], fill=(255, 255, 255, 255))


def riquadro(d, x0, y0, x1, y1, raggio=RAGGIO):
    d.rounded_rectangle([x0 * SCALA, y0 * SCALA, x1 * SCALA, y1 * SCALA],
                        radius=raggio * SCALA, outline=(255, 255, 255, 255),
                        width=round(TRATTO * SCALA))


# --- i quindici segni ---------------------------------------------------------

def indietro(d):
    """Freccia: asta lunga e punta aperta. La punta e' a 50 gradi, non a 45:
    a 45 sembra il chevron di sistema, e questo deve essere un'altra cosa."""
    linea(d, (26, 16), (8, 16))
    linea(d, (15.5, 8.5), (7.6, 16), (15.5, 23.5))


def cerca(d):
    """Lente: il cerchio sta alto a sinistra e il manico scende a 45."""
    cerchio(d, 14, 14, 7)
    linea(d, (19.4, 19.4), (25.5, 25.5))


def chiudi(d):
    linea(d, (9, 9), (23, 23))
    linea(d, (23, 9), (9, 23))


def piu(d):
    linea(d, (16, 8), (16, 24))
    linea(d, (8, 16), (24, 16))


def meno(d):
    linea(d, (8, 16), (24, 16))


def griglia(d):
    """Quattro riquadri: e' la vista a tessere.

    Al primo tentativo erano 7 di lato con raggio 2,4, e restava un puntino
    bianco nel mezzo: con un tratto di 2,6 un quadrato di 7 non ha abbastanza
    interno, e l'arrotondamento se lo mangiava. Nove di lato, raggio 1,8.
    """
    for x in (5.5, 17.5):
        for y in (5.5, 17.5):
            riquadro(d, x, y, x + 9, y + 9, raggio=1.8)


def elenco(d):
    """Tre righe, ognuna con la sua tacca a sinistra: e' la scheda-riga vista da
    lontano, foto a sinistra e testo a destra."""
    for y in (9, 16, 23):
        linea(d, (7, y), (9.6, y))
        linea(d, (13.5, y), (25, y))


def matita(d):
    """Corpo obliquo e punta: il taglio in fondo e' la mina."""
    linea(d, (20.5, 7.5), (24.5, 11.5))
    linea(d, (22.5, 5.5), (8.5, 19.5), (7, 25), (12.5, 23.5), (26.5, 9.5), chiusa=False)
    linea(d, (22.5, 5.5), (26.5, 9.5))


def libro(d):
    """Due pagine aperte con il dorso in mezzo: e' «tutte le ricette»."""
    linea(d, (16, 10), (16, 25))
    linea(d, (16, 10), (10, 7.5), (5.5, 7.5), (5.5, 22.5), (10, 22.5), (16, 25))
    linea(d, (16, 10), (22, 7.5), (26.5, 7.5), (26.5, 22.5), (22, 22.5), (16, 25))


def vassoio(d):
    """Il vassoio dove si posa quello che non e' ancora sistemato: e' «senza
    categoria».

    Aperto in cima, di proposito: la prima versione era un rettangolo chiuso
    con una linea in mezzo e sembrava una valigia. Un vassoio si riconosce
    perche' non ha coperchio.
    """
    linea(d, (4.5, 9), (7.5, 24.5), (24.5, 24.5), (27.5, 9))
    linea(d, (6.4, 19), (25.6, 19))


def cartella(d):
    """Cartella con la linguetta: e' «gestisci le categorie»."""
    linea(d, (5.5, 24.5), (5.5, 9), (13, 9), (15.5, 12), (26.5, 12), (26.5, 24.5), chiusa=True)


def altro(d):
    """Tre punti: il menu dell'importa ed esporta."""
    for x in (9, 16, 23):
        disco(d, x, 16, TRATTO / 2)


def ingranaggio(d):
    """Ingranaggio: un profilo dentato chiuso e il mozzo. E' «impostazioni».

    La prima versione era un cerchio con otto trattini radiali, e a schermo
    era un sole. Un ingranaggio si riconosce dal bordo che sale e scende: qui
    otto denti, con i fianchi corti e i vuoti larghi quanto i pieni, e il
    mozzo vuoto in mezzo.
    """
    fuori, dentro, denti = 12.5, 9.2, 8
    passo = 2 * math.pi / denti
    punti = []
    for k in range(denti):
        a = passo * k
        for frazione, raggio in ((-0.30, dentro), (-0.17, fuori), (0.17, fuori), (0.30, dentro)):
            t = a + frazione * passo
            punti.append((16 + raggio * math.cos(t), 16 + raggio * math.sin(t)))
    linea(d, *punti, chiusa=True)
    cerchio(d, 16, 16, 3.6)


def condividiios(d):
    """Condividi, come lo disegna iOS: un riquadro aperto in cima e una freccia
    che ne esce verso l'alto. Non e' un'icona nostra: e' quella che chi ha un
    iPhone riconosce a colpo d'occhio, ridisegnata col nostro tratto."""
    linea(d, (12.5, 12.5), (9.5, 12.5), (9.5, 26.5), (22.5, 26.5), (22.5, 12.5), (19.5, 12.5))
    linea(d, (16, 19.5), (16, 5.5))
    linea(d, (11.5, 10), (16, 5.5), (20.5, 10))


def condividiandroid(d):
    """Condividi, come lo disegna Android: tre nodi uniti da due segmenti.
    Stesso motivo del gemello iOS: e' il glifo che quel sistema usa ovunque."""
    for cx, cy in ((22.5, 8), (9.5, 16), (22.5, 24)):
        disco(d, cx, cy, 3.3)
    linea(d, (12.4, 14.2), (19.6, 9.8))
    linea(d, (12.4, 17.8), (19.6, 22.2))


SEGNI = {
    'indietro': indietro, 'cerca': cerca, 'chiudi': chiudi, 'piu': piu,
    'meno': meno, 'griglia': griglia, 'elenco': elenco, 'matita': matita,
    'libro': libro, 'vassoio': vassoio, 'cartella': cartella, 'altro': altro,
    'ingranaggio': ingranaggio, 'condividiios': condividiios, 'condividiandroid': condividiandroid,
}


def main():
    USCITA.mkdir(parents=True, exist_ok=True)
    for nome, disegna in SEGNI.items():
        im = tela()
        disegna(ImageDraw.Draw(im))
        im = im.resize((LATO, LATO), Image.LANCZOS)
        f = USCITA / f'{nome}.png'
        im.save(f, optimize=True)
        print(f'  {nome:10} {f.stat().st_size:5} byte')
    print(f'{len(SEGNI)} segni in {USCITA}')


if __name__ == '__main__':
    main()
