diff --git a/CREDITS.md b/CREDITS.md index 12f8660..cba0206 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -21,6 +21,15 @@ Les contenus pédagogiques (leçons, textes) sont sous licence CC BY-SA 4.0. https://github.com/clavierhaus/gnubg-android ; son fichier `jni-bridge/src/stubs.c` a servi de repère pour isoler le cœur de gnubg. +## Identité visuelle + +- **Alegreya Sans**, Copyright 2013 The Alegreya Sans Project Authors + (Juan Pablo del Peral, Huerta Tipográfica), licence + SIL Open Font License 1.1 — https://github.com/google/fonts/tree/main/ofl/alegreyasans + Ses tracés (graisse Bold) composent le logotype, dans `docs/identite/logo/`. +- **fontTools** (MIT), utilisé par `docs/identite/generer-logo.py` pour + convertir le texte du logotype en tracés. + ## Outils de développement - **ShellCheck** (GPLv3), analyse des scripts shell, utilisé via Docker. diff --git a/docs/adr/0010-identite-visuelle.md b/docs/adr/0010-identite-visuelle.md new file mode 100644 index 0000000..62119a9 --- /dev/null +++ b/docs/adr/0010-identite-visuelle.md @@ -0,0 +1,62 @@ +# 0010. Identité visuelle : symbole, logotype, palette + +- **Statut** : accepté +- **Date** : 2026-11-03 +- **Décideurs** : Pierre + +## Contexte + +Le brief créatif (https://claude.ai/artifact/36WMwZWqvMxXvSSVBJTgwd) fixe la +personnalité d'Ustagammon (transmission, chaleur, maîtrise), une palette de +départ (bois sombre, ivoire, un accent unique à choisir entre rouge brique et +turquoise) et quatre pistes de symbole, avec une contrainte : un 8 caché, clin +d'œil à l'Atelier du Huit, qui se découvre au second regard. + +Les propositions ont été faites en trois tours sur une planche de travail +(https://claude.ai/artifact/Cck1SZCrKeYWbqJLhbwWNp), chacune testée en +couleur, en monochrome et en taille réelle d'icône (48 et 24 px). + +## Décision + +- **Accent** : rouge brique, `#A8432B` en thème clair, `#D0694B` en thème + sombre. La turquoise est écartée. +- **Symbole** (variante « P3d ») : deux pions vus de trois quarts, avec leur + tranche, posés sur une flèche du plateau en brique. Le maître (noyer) est + devant, l'apprenti (ivoire, filet brique) derrière ; leurs silhouettes + dessinent le 8. +- **Logotype** (variante « L3 ») : « Ustagammon » en Alegreya Sans Bold, dont + les deux « m » sont redessinés en quatre flèches au trait qui alternent + noyer et brique, comme les flèches d'un plateau. En monochrome, les quatre + flèches prennent la couleur du texte. +- **Icône d'application** : symbole sur fond ivoire `#F3EAD8`. +- **Police** : Alegreya Sans (SIL Open Font License 1.1), pour le logotype et, + pour l'interface. Pierre a validé cette dépendance, ainsi que fontTools + (MIT), outil de développement qui sert à régénérer le logotype, le + 2026-11-03. +- Les fichiers de référence sont dans `docs/identite/logo/`, produits par + `docs/identite/generer-logo.py` ; ils sont sous licence CC BY-SA 4.0. + +## Alternatives écartées + +- **Accent turquoise** : plus froid ; la brique s'accorde mieux au bois et au + café. +- **Piste B (le videau)** : écartée au premier tour, au profit des pions. +- **Pions vus de face (A1) ou en léger relief sans flèche (P1)** : appréciés, + mais la flèche rattache le symbole au plateau et fait écho au logotype. +- **Filets brique sur les deux pions (P3c)** : trop de brique dans un petit + symbole. +- **Logotype aux flèches pleines (L2)** : les « mm » s'y lisaient moins vite ; + le trait (L1) garde la lisibilité, et l'alternance de couleur (L3) apporte + le rappel du plateau. +- **Fond d'icône noyer** : moins lumineux sur un écran d'accueil. + +## Conséquences + +- Le design system (couleurs des deux thèmes, typographie, composants) part de + cette palette et de cette police. +- La police Alegreya Sans devra être embarquée dans la PWA (fonctionnement hors + ligne) ; sa licence OFL le permet, avec sa notice. +- Le favicon utilise un dessin simplifié du symbole, sans filets, illisibles + en dessous de 32 px. +- Les icônes Android (premier plan, arrière-plan, monochrome) sont fournies en + SVG ; leur conversion en `VectorDrawable` se fera avec l'application Android. diff --git a/docs/adr/README.md b/docs/adr/README.md index ba46966..0bc531c 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -16,3 +16,4 @@ l'ancien passe au statut « remplacé par NNNN ». Voir l'ADR 0001. | [0007](0007-architecture-et-conventions-de-code.md) | Architecture et conventions de code | accepté | | [0008](0008-glossaire-et-scenarios-source-unique.md) | Glossaire et scénarios : une source unique pour le code, l'aide et les tests | accepté | | [0009](0009-socle-pwa-typescript.md) | Socle : PWA en TypeScript, moteur en WebAssembly | accepté | +| [0010](0010-identite-visuelle.md) | Identité visuelle : symbole, logotype, palette | accepté | diff --git a/docs/identite/README.md b/docs/identite/README.md new file mode 100644 index 0000000..af8d616 --- /dev/null +++ b/docs/identite/README.md @@ -0,0 +1,48 @@ +# Identité visuelle d'Ustagammon + +Décision et historique des choix : ADR 0010 (`docs/adr/0010-identite-visuelle.md`). +Brief créatif : https://claude.ai/artifact/36WMwZWqvMxXvSSVBJTgwd + +Les dessins de ce dossier sont sous licence **CC BY-SA 4.0**. Le logotype +utilise des tracés de la police **Alegreya Sans Bold** (Juan Pablo del Peral, +Huerta Tipográfica), sous licence SIL Open Font License 1.1. + +## Fichiers (`logo/`) + +| Fichier | Usage | +| --- | --- | +| `symbole-couleur.svg` | Symbole seul, sur fond clair | +| `symbole-sombre.svg` | Symbole seul, sur fond sombre | +| `symbole-noir.svg`, `symbole-blanc.svg` | Monochromes, fond transparent | +| `logotype-*.svg` | Le nom seul, mêmes quatre déclinaisons | +| `logo-horizontal-*.svg` | Symbole et nom côte à côte, mêmes déclinaisons | +| `icone.svg`, `icone-192.png`, `icone-512.png` | Icône de la PWA (`purpose: any`) | +| `icone-maskable.svg`, `icone-maskable-192.png`, `icone-maskable-512.png` | Icône de la PWA (`purpose: maskable`), symbole dans la zone de sécurité | +| `apple-touch-icon-180.png` | Ajout à l'écran d'accueil sur iPhone (via le navigateur) | +| `favicon.svg`, `favicon-16.png`, `favicon-32.png`, `favicon-48.png` | Onglet du navigateur ; dessin simplifié, sans filets | +| `android-premier-plan.svg`, `android-arriere-plan.svg`, `android-monochrome.svg` | Couches de l'icône adaptative Android (canevas 108 dp), pour plus tard | + +## Couleurs du logo + +| Rôle | Clair | Sombre | +| --- | --- | --- | +| Noyer (pion maître) | `#4A2E1F` | idem | +| Ivoire (pion apprenti, fond d'icône) | `#F3EAD8` | idem | +| Brique (flèche, filet, « mm ») | `#A8432B` | `#D0694B` (flèches du logotype), `#C9583C` (flèche du symbole) | +| Texte du logotype | `#2B1D15` | `#F3EAD8` | + +## Régénérer + +Les fichiers sont versionnés ; il n'est utile de les régénérer qu'en cas de +retouche du dessin. Il faut Python 3 avec fontTools (licence MIT) et la police +Alegreya Sans Bold : + +```sh +mkdir -p build/police +curl -sSLf -o build/police/AlegreyaSans-Bold.ttf \ + https://github.com/google/fonts/raw/main/ofl/alegreyasans/AlegreyaSans-Bold.ttf +python3 -I docs/identite/generer-logo.py build/police/AlegreyaSans-Bold.ttf docs/identite/logo +``` + +Les PNG sont des rendus des SVG par Chrome sans interface +(`google-chrome --headless --screenshot`), à la taille indiquée dans leur nom. diff --git a/docs/identite/generer-logo.py b/docs/identite/generer-logo.py new file mode 100644 index 0000000..c4a77eb --- /dev/null +++ b/docs/identite/generer-logo.py @@ -0,0 +1,192 @@ +"""Génère les fichiers SVG de l'identité Ustagammon : symbole P3d, logotype L3. + +Le symbole : deux pions vus de trois quarts (le maître en noyer devant, +l'apprenti en ivoire derrière) posés sur une flèche brique ; leurs deux +silhouettes dessinent un 8, clin d'œil à l'Atelier du Huit. +Le logotype : « Ustagammon » en Alegreya Sans Bold (SIL OFL), dont les deux +« m » sont redessinés en quatre flèches au trait, alternant noyer et brique. + +Usage : python3 -I generer-logo.py +Licence des dessins produits : CC BY-SA 4.0. +""" +import os +import sys + +from fontTools.pens.svgPathPen import SVGPathPen +from fontTools.ttLib import TTFont + +FONT, OUT = sys.argv[1], sys.argv[2] +os.makedirs(OUT, exist_ok=True) + +# ---------- Palettes ---------- +# d/ds/de : face, tranche et filet du pion noyer ; l/ls : face et tranche du pion ivoire ; +# o : contour du pion ivoire ; a : flèche et filet brique ; sep : liseré du pion noyer ; +# txt / acc : texte du logotype et flèches brique des « mm ». +COULEUR = dict(d="#4A2E1F", ds="#2B1A10", de="#7A5038", l="#F3EAD8", ls="#CDB898", + o="#2B1A10", a="#A8432B", sep="#F3EAD8", txt="#2B1D15", acc="#A8432B") +SOMBRE = dict(COULEUR, a="#C9583C", txt="#F3EAD8", acc="#D0694B") +# Monochromes : I = encre, H = trou (transparent) +MONO = dict(d="I", ds="I", de="H", l="H", ls="I", o="I", a="I", sep="H", txt="I", acc="I") + + +def symbol(p, simple=False): + """Symbole P3d dans une boîte de 64 × 64. `simple` : sans filets, pour le favicon.""" + s = [f''] + ow = 3 if simple else 2.2 + s.append(f'') + s.append(f'') + if not simple: + s.append(f'') + sw = 2.6 if simple else 2 + s.append(f'') + s.append(f'') + if not simple: + s.append(f'') + return "".join(s) + + +# ---------- Logotype ---------- +font = TTFont(FONT) +gs = font.getGlyphSet() +cmap = font.getBestCmap() +hmtx = font["hmtx"] +XH = font["OS/2"].sxHeight # hauteur d'x (465 pour Alegreya Sans Bold) +STROKE = 0.21 * XH # épaisseur du trait des flèches, proche du fût des lettres +MM_W = 3.0 * XH # largeur du bloc « mm » +GAP = 20 + + +def glyph(ch, x, fill): + g = cmap[ord(ch)] + pen = SVGPathPen(gs) + gs[g].draw(pen) + return f'', hmtx[g][0] + + +def inter(p1, d1, p2, d2): + """Intersection de deux droites données par un point et une direction.""" + (x1, y1), (dx1, dy1), (x2, y2), (dx2, dy2) = p1, d1, p2, d2 + det = dx1 * -dy2 - dy1 * -dx2 + t = ((x2 - x1) * -dy2 - (y2 - y1) * -dx2) / det + return (x1 + t * dx1, y1 + t * dy1) + + +def chevron(x0, x1, top): + """Flèche « ^ » au trait : pieds coupés sur la ligne de base (y = 0), sommet en onglet.""" + peak = ((x0 + x1) / 2, -top) + h = STROKE / 2 + lines = [] + for a, b in (((x0, 0), peak), (peak, (x1, 0))): + dx, dy = b[0] - a[0], b[1] - a[1] + n = (dx * dx + dy * dy) ** 0.5 + nx, ny = -dy / n, dx / n + lines.append(((a[0] + nx * h, a[1] + ny * h), (dx, dy), (a[0] - nx * h, a[1] - ny * h))) + (lo, ld, li), (ro, rd, ri) = lines + base = ((0, 0), (1, 0)) + l_ext, l_int = (li, lo) if li[1] < lo[1] else (lo, li) + r_ext, r_int = (ri, ro) if ri[1] < ro[1] else (ro, ri) + pts = [inter(l_ext, ld, *base), inter(l_ext, ld, r_ext, rd), inter(r_ext, rd, *base), + inter(r_int, rd, *base), inter(l_int, ld, r_int, rd), inter(l_int, ld, *base)] + return " ".join(f"{x:.1f},{y:.1f}" for x, y in pts) + + +def logotype(p, x=0.0): + parts = [] + for ch in "Ustaga": + s, adv = glyph(ch, x, p["txt"]) + parts.append(s) + x += adv + x += GAP + unit = MM_W / 300 # coordonnées de la maquette : boîte de 300 × 100 + top = XH * 1.06 # léger dépassement de la hauteur d'x, comme une lettre pointue + for i, (a, b) in enumerate(((8, 74), (74, 140), (160, 226), (226, 292))): + fill = p["txt"] if i % 2 == 0 else p["acc"] + parts.append(f'') + x += MM_W + GAP + for ch in "on": + s, adv = glyph(ch, x, p["txt"]) + parts.append(s) + x += adv + return "".join(parts), x + + +ASC, DESC = 680, 240 # marges verticales autour de la ligne de base + + +def mono_wrap(body, vb, ink, mid): + """Transforme un dessin encre/trou en dessin d'une seule couleur, trous transparents.""" + body = body.replace('"I"', '"#fff"').replace('"H"', '"#000"') + x, y, w, h = vb + return (f'' + f'{body}' + f'') + + +def svg(vb, body, title): + return (f'{title}{body}\n') + + +def write(name, content): + with open(os.path.join(OUT, name), "w", encoding="utf-8") as f: + f.write(content) + + +VARIANTS = {"couleur": COULEUR, "sombre": SOMBRE, "noir": "#000000", "blanc": "#FFFFFF"} + + +def palette(v): + return VARIANTS[v] if isinstance(VARIANTS[v], dict) else MONO + + +def ink(body, v): + return body if isinstance(VARIANTS[v], dict) else body.replace('"I"', f'"{VARIANTS[v]}"') + + +# ---------- Symbole ---------- +for v in VARIANTS: + vb = (0, 0, 64, 64) + body = symbol(palette(v)) + if not isinstance(VARIANTS[v], dict): + body = mono_wrap(body, vb, VARIANTS[v], "m") + write(f"symbole-{v}.svg", svg(vb, body, "Ustagammon")) + +# ---------- Logotype seul ---------- +for v in VARIANTS: + body, w = logotype(palette(v)) + write(f"logotype-{v}.svg", svg((-10, -ASC, round(w + 20), ASC + DESC), ink(body, v), "Ustagammon")) + +# ---------- Logo horizontal ---------- +SYM = 1450 # hauteur du symbole, pour un corps de 1000 +CENTER = -322 # centre optique : mi-hauteur des capitales +for v in VARIANTS: + p = palette(v) + sym = f'{symbol(p)}' + text, w = logotype(p, x=SYM + 280) + vb = (-20, CENTER - SYM / 2 - 20, round(w + 40), SYM + 40) + if not isinstance(VARIANTS[v], dict): + sym = mono_wrap(sym, vb, VARIANTS[v], "m") + write(f"logo-horizontal-{v}.svg", svg(vb, sym + ink(text, v), "Ustagammon")) + +# ---------- Icônes ---------- +IVOIRE = "#F3EAD8" + + +def placed(frac, size, pal=COULEUR, simple=False): + """Symbole centré dans un carré de `size`, occupant la fraction `frac` du côté.""" + s = size * frac / 64 + off = (size - 64 * s) / 2 + return f'{symbol(pal, simple)}' + + +# Icône « any » : tuile ivoire aux coins arrondis, transparente autour +write("icone.svg", svg((0, 0, 512, 512), f'' + placed(0.70, 512), "Ustagammon")) +# Icône « maskable » : fond plein, symbole dans la zone de sécurité (cercle de 80 %) +write("icone-maskable.svg", svg((0, 0, 512, 512), f'' + placed(0.56, 512), "Ustagammon")) +# Favicon : dessin simplifié (sans filets), traits épaissis, fond transparent +write("favicon.svg", svg((0, 0, 64, 64), symbol(COULEUR, simple=True), "Ustagammon")) +# Android, icône adaptative : canevas de 108 × 108 dp, zone sûre de 66 dp au centre +write("android-premier-plan.svg", svg((0, 0, 108, 108), placed(0.58, 108), "Ustagammon, premier plan")) +write("android-arriere-plan.svg", svg((0, 0, 108, 108), f'', "Ustagammon, arrière-plan")) +write("android-monochrome.svg", svg((0, 0, 108, 108), mono_wrap(placed(0.58, 108, MONO), (0, 0, 108, 108), "#000000", "m"), "Ustagammon, monochrome")) +print("\n".join(sorted(os.listdir(OUT)))) diff --git a/docs/identite/logo/android-arriere-plan.svg b/docs/identite/logo/android-arriere-plan.svg new file mode 100644 index 0000000..16aa051 --- /dev/null +++ b/docs/identite/logo/android-arriere-plan.svg @@ -0,0 +1 @@ +Ustagammon, arrière-plan diff --git a/docs/identite/logo/android-monochrome.svg b/docs/identite/logo/android-monochrome.svg new file mode 100644 index 0000000..531890e --- /dev/null +++ b/docs/identite/logo/android-monochrome.svg @@ -0,0 +1 @@ +Ustagammon, monochrome diff --git a/docs/identite/logo/android-premier-plan.svg b/docs/identite/logo/android-premier-plan.svg new file mode 100644 index 0000000..2ef9493 --- /dev/null +++ b/docs/identite/logo/android-premier-plan.svg @@ -0,0 +1 @@ +Ustagammon, premier plan diff --git a/docs/identite/logo/apple-touch-icon-180.png b/docs/identite/logo/apple-touch-icon-180.png new file mode 100644 index 0000000..f1d68a1 Binary files /dev/null and b/docs/identite/logo/apple-touch-icon-180.png differ diff --git a/docs/identite/logo/favicon-16.png b/docs/identite/logo/favicon-16.png new file mode 100644 index 0000000..7d51aa5 Binary files /dev/null and b/docs/identite/logo/favicon-16.png differ diff --git a/docs/identite/logo/favicon-32.png b/docs/identite/logo/favicon-32.png new file mode 100644 index 0000000..cd26a6e Binary files /dev/null and b/docs/identite/logo/favicon-32.png differ diff --git a/docs/identite/logo/favicon-48.png b/docs/identite/logo/favicon-48.png new file mode 100644 index 0000000..b5c594a Binary files /dev/null and b/docs/identite/logo/favicon-48.png differ diff --git a/docs/identite/logo/favicon.svg b/docs/identite/logo/favicon.svg new file mode 100644 index 0000000..e27a19d --- /dev/null +++ b/docs/identite/logo/favicon.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/icone-192.png b/docs/identite/logo/icone-192.png new file mode 100644 index 0000000..2c59280 Binary files /dev/null and b/docs/identite/logo/icone-192.png differ diff --git a/docs/identite/logo/icone-512.png b/docs/identite/logo/icone-512.png new file mode 100644 index 0000000..130fb99 Binary files /dev/null and b/docs/identite/logo/icone-512.png differ diff --git a/docs/identite/logo/icone-maskable-192.png b/docs/identite/logo/icone-maskable-192.png new file mode 100644 index 0000000..e62aaa5 Binary files /dev/null and b/docs/identite/logo/icone-maskable-192.png differ diff --git a/docs/identite/logo/icone-maskable-512.png b/docs/identite/logo/icone-maskable-512.png new file mode 100644 index 0000000..839b01d Binary files /dev/null and b/docs/identite/logo/icone-maskable-512.png differ diff --git a/docs/identite/logo/icone-maskable.svg b/docs/identite/logo/icone-maskable.svg new file mode 100644 index 0000000..6658c92 --- /dev/null +++ b/docs/identite/logo/icone-maskable.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/icone.svg b/docs/identite/logo/icone.svg new file mode 100644 index 0000000..b65c68e --- /dev/null +++ b/docs/identite/logo/icone.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logo-horizontal-blanc.svg b/docs/identite/logo/logo-horizontal-blanc.svg new file mode 100644 index 0000000..0c3213e --- /dev/null +++ b/docs/identite/logo/logo-horizontal-blanc.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logo-horizontal-couleur.svg b/docs/identite/logo/logo-horizontal-couleur.svg new file mode 100644 index 0000000..f21e21a --- /dev/null +++ b/docs/identite/logo/logo-horizontal-couleur.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logo-horizontal-noir.svg b/docs/identite/logo/logo-horizontal-noir.svg new file mode 100644 index 0000000..3373a35 --- /dev/null +++ b/docs/identite/logo/logo-horizontal-noir.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logo-horizontal-sombre.svg b/docs/identite/logo/logo-horizontal-sombre.svg new file mode 100644 index 0000000..d563055 --- /dev/null +++ b/docs/identite/logo/logo-horizontal-sombre.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logotype-blanc.svg b/docs/identite/logo/logotype-blanc.svg new file mode 100644 index 0000000..4cff072 --- /dev/null +++ b/docs/identite/logo/logotype-blanc.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logotype-couleur.svg b/docs/identite/logo/logotype-couleur.svg new file mode 100644 index 0000000..f6870ed --- /dev/null +++ b/docs/identite/logo/logotype-couleur.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logotype-noir.svg b/docs/identite/logo/logotype-noir.svg new file mode 100644 index 0000000..2c225e9 --- /dev/null +++ b/docs/identite/logo/logotype-noir.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/logotype-sombre.svg b/docs/identite/logo/logotype-sombre.svg new file mode 100644 index 0000000..7df90ab --- /dev/null +++ b/docs/identite/logo/logotype-sombre.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/symbole-blanc.svg b/docs/identite/logo/symbole-blanc.svg new file mode 100644 index 0000000..480c8e9 --- /dev/null +++ b/docs/identite/logo/symbole-blanc.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/symbole-couleur.svg b/docs/identite/logo/symbole-couleur.svg new file mode 100644 index 0000000..2d6a536 --- /dev/null +++ b/docs/identite/logo/symbole-couleur.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/symbole-noir.svg b/docs/identite/logo/symbole-noir.svg new file mode 100644 index 0000000..764c6ca --- /dev/null +++ b/docs/identite/logo/symbole-noir.svg @@ -0,0 +1 @@ +Ustagammon diff --git a/docs/identite/logo/symbole-sombre.svg b/docs/identite/logo/symbole-sombre.svg new file mode 100644 index 0000000..f7e7bf0 --- /dev/null +++ b/docs/identite/logo/symbole-sombre.svg @@ -0,0 +1 @@ +Ustagammon