hachure 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hachure-0.3.0/LICENSE +21 -0
- hachure-0.3.0/PKG-INFO +572 -0
- hachure-0.3.0/README.md +545 -0
- hachure-0.3.0/hachure/__init__.py +5 -0
- hachure-0.3.0/hachure/__main__.py +5 -0
- hachure-0.3.0/hachure/charsets.py +194 -0
- hachure-0.3.0/hachure/cli.py +697 -0
- hachure-0.3.0/hachure/color.py +161 -0
- hachure-0.3.0/hachure/edges.py +117 -0
- hachure-0.3.0/hachure/export.py +304 -0
- hachure-0.3.0/hachure/media/__init__.py +1 -0
- hachure-0.3.0/hachure/media/image.py +91 -0
- hachure-0.3.0/hachure/media/video.py +609 -0
- hachure-0.3.0/hachure/menu.py +952 -0
- hachure-0.3.0/hachure/render.py +311 -0
- hachure-0.3.0/hachure/renderers/__init__.py +38 -0
- hachure-0.3.0/hachure/renderers/blackhole.py +68 -0
- hachure-0.3.0/hachure/renderers/common.py +12 -0
- hachure-0.3.0/hachure/renderers/cube.py +129 -0
- hachure-0.3.0/hachure/renderers/donut.py +65 -0
- hachure-0.3.0/hachure/renderers/planet.py +48 -0
- hachure-0.3.0/hachure/renderers/sphere.py +34 -0
- hachure-0.3.0/hachure/terminal.py +286 -0
- hachure-0.3.0/hachure/tone.py +93 -0
- hachure-0.3.0/hachure.egg-info/PKG-INFO +572 -0
- hachure-0.3.0/hachure.egg-info/SOURCES.txt +41 -0
- hachure-0.3.0/hachure.egg-info/dependency_links.txt +1 -0
- hachure-0.3.0/hachure.egg-info/entry_points.txt +2 -0
- hachure-0.3.0/hachure.egg-info/requires.txt +5 -0
- hachure-0.3.0/hachure.egg-info/top_level.txt +1 -0
- hachure-0.3.0/pyproject.toml +52 -0
- hachure-0.3.0/setup.cfg +4 -0
- hachure-0.3.0/tests/test_charsets.py +95 -0
- hachure-0.3.0/tests/test_cli.py +71 -0
- hachure-0.3.0/tests/test_edges.py +101 -0
- hachure-0.3.0/tests/test_export.py +59 -0
- hachure-0.3.0/tests/test_image.py +41 -0
- hachure-0.3.0/tests/test_menu.py +624 -0
- hachure-0.3.0/tests/test_render.py +214 -0
- hachure-0.3.0/tests/test_renderers.py +23 -0
- hachure-0.3.0/tests/test_terminal.py +147 -0
- hachure-0.3.0/tests/test_tone.py +81 -0
- hachure-0.3.0/tests/test_video.py +185 -0
hachure-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Niladri Pal and Talal Alqahs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hachure-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hachure
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Rend images, vidéos, caméras et art 3D procédural en ASCII dans un terminal
|
|
5
|
+
Author: Josué AGBETA
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Josueagbeta0/hachure-ascii
|
|
8
|
+
Project-URL: Repository, https://github.com/Josueagbeta0/hachure-ascii
|
|
9
|
+
Project-URL: Issues, https://github.com/Josueagbeta0/hachure-ascii/issues
|
|
10
|
+
Keywords: ascii-art,terminal,video,camera,graphics,ansi,truecolor,tui
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
17
|
+
Classifier: Topic :: Multimedia :: Video
|
|
18
|
+
Classifier: Natural Language :: French
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: Pillow>=10.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# hachure
|
|
29
|
+
|
|
30
|
+
Rendre des images, des vidéos, une caméra en direct et des scènes 3D procédurales **en caractères, dans un terminal**.
|
|
31
|
+
|
|
32
|
+
La *hachure* est la technique qui rend le ton et la forme par des traits directionnels. C'est
|
|
33
|
+
littéralement ce que fait ce moteur : une rampe de luminosité pour le ton, et un tenseur de
|
|
34
|
+
structure qui choisit `-`, `\`, `|` ou `/` par cellule pour la forme.
|
|
35
|
+
|
|
36
|
+
Tape `hachure`, sans rien d'autre, et navigue au clavier. Aucun argument à retenir, aucun chemin à
|
|
37
|
+
taper : un menu, un navigateur de fichiers, des réglages qui se parcourent.
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
pixels source ou géométrie 3D
|
|
41
|
+
↓
|
|
42
|
+
luminosité / éclairage
|
|
43
|
+
↓
|
|
44
|
+
niveaux automatiques et gamma
|
|
45
|
+
↓
|
|
46
|
+
orientation du contour par cellule
|
|
47
|
+
↓
|
|
48
|
+
correspondance caractère ou bloc
|
|
49
|
+
↓
|
|
50
|
+
couleur ANSI facultative
|
|
51
|
+
↓
|
|
52
|
+
terminal
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Sommaire
|
|
58
|
+
|
|
59
|
+
[Le menu](#le-menu) · [Installation](#installation) · [En ligne de commande](#en-ligne-de-commande) ·
|
|
60
|
+
[Le mode caractère](#tirer-le-meilleur-du-mode-caractère) · [Image](#rendu-dimage) ·
|
|
61
|
+
[Vidéo](#rendu-vidéo) · [Caméra](#capture-caméra) · [Enregistrement](#enregistrement) ·
|
|
62
|
+
[Démos](#démos-procédurales) · [Couleurs](#couleurs) · [Performance](#notes-de-performance) ·
|
|
63
|
+
[Développement](#développement) · [Crédits](#crédits)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Le menu
|
|
68
|
+
|
|
69
|
+
Une seule commande, sans argument :
|
|
70
|
+
|
|
71
|
+
```powershell
|
|
72
|
+
hachure
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
hachure · rendu d'images, de vidéos et de 3D en caractères
|
|
77
|
+
|
|
78
|
+
Image fixe convertir une photo en caractères
|
|
79
|
+
› Vidéo lire un fichier vidéo
|
|
80
|
+
Caméra diffuser une caméra en direct
|
|
81
|
+
Démo procédurale cube, sphère, tore, planète, trou noir
|
|
82
|
+
Diagnostic dépendances et capacités du terminal
|
|
83
|
+
Moteurs disponibles ce que le projet sait rendre
|
|
84
|
+
Calibrer une rampe mesurer une police à chasse fixe
|
|
85
|
+
Quitter
|
|
86
|
+
|
|
87
|
+
↑↓ déplacer · Entrée valider · Échap revenir
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Rien ne se tape.** Choisis une source et un navigateur de fichiers s'ouvre, filtré sur les formats
|
|
91
|
+
que la commande sait lire, avec la taille de chaque fichier :
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
Quelle vidéo ?
|
|
95
|
+
C:\Users\moi\Videos
|
|
96
|
+
|
|
97
|
+
› .. dossier parent
|
|
98
|
+
archives/ dossier
|
|
99
|
+
vacances/ dossier
|
|
100
|
+
concert.mp4 84.2 Mo
|
|
101
|
+
timelapse.mkv 12.7 Mo
|
|
102
|
+
[ changer de disque ]
|
|
103
|
+
[ annuler ]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Puis un écran de réglages, où chaque ligne se change sans jamais saisir de valeur — `Espace` ou
|
|
107
|
+
←→ pour passer à la suivante, `Entrée` pour ouvrir la liste complète :
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
Réglages · video
|
|
111
|
+
|
|
112
|
+
Largeur maximale 160
|
|
113
|
+
Hauteur maximale défaut
|
|
114
|
+
Ajustement cover
|
|
115
|
+
Couleur oui
|
|
116
|
+
› Contours (--edges) oui
|
|
117
|
+
Niveaux automatiques oui
|
|
118
|
+
Jeu de caractères detailed
|
|
119
|
+
Géométrie de cellule char
|
|
120
|
+
Inverser la rampe non
|
|
121
|
+
Images par seconde 24
|
|
122
|
+
Couper le son non
|
|
123
|
+
Lire en boucle non
|
|
124
|
+
Départ en secondes défaut
|
|
125
|
+
Durée en secondes 30
|
|
126
|
+
Enregistrer le rendu concert.mp4
|
|
127
|
+
|
|
128
|
+
Lancer le rendu
|
|
129
|
+
Annuler
|
|
130
|
+
|
|
131
|
+
hachure … --width 160 --fit cover --color --edges --auto-levels --charset detailed --fps 24 …
|
|
132
|
+
↑↓ déplacer · Espace/←→ changer · Entrée ouvrir · Échap revenir
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
La ligne du bas est le point important : **le menu ne fait que composer une ligne de commande**, il
|
|
136
|
+
te la montre, puis il la joue. Tu repars donc en sachant quoi retaper directement :
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
$ hachure video concert.mp4 --width 160 --fit cover --color --edges --auto-levels --charset detailed
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Un réglage laissé sur `défaut` n'est pas transmis : c'est la valeur par défaut de la CLI qui
|
|
143
|
+
s'applique, jamais une copie figée dans le menu.
|
|
144
|
+
|
|
145
|
+
`hachure menu` ouvre le même écran explicitement. Quand l'entrée ou la sortie est redirigée — un
|
|
146
|
+
pipe, un script, Git Bash, où Python ne reconnaît pas le terminal — la navigation aux flèches
|
|
147
|
+
devient impossible : le menu retombe alors sur des listes numérotées, et toutes les sous-commandes
|
|
148
|
+
ci-dessous restent utilisables directement.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Installation
|
|
153
|
+
|
|
154
|
+
### Prérequis
|
|
155
|
+
|
|
156
|
+
| Composant | Rôle |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| **Python ≥ 3.10** | La CI valide 3.10 et 3.14. NumPy et Pillow s'installent automatiquement. |
|
|
159
|
+
| **ffmpeg** | Décode la vidéo et la caméra en images brutes. Requis par `video` et `camera` seulement. |
|
|
160
|
+
| **ffprobe** | Lit dimensions et rotation de la source, pour redresser une vidéo filmée verticalement. |
|
|
161
|
+
| **ffplay** | Lit la piste audio en parallèle. Facultatif : `--no-audio` s'en passe. |
|
|
162
|
+
| **Police à chasse fixe** | Doit contenir `▀` et `▄` pour le mode demi-bloc. Cascadia Mono, Consolas, DejaVu Sans Mono, Menlo. |
|
|
163
|
+
| **Terminal truecolor** | Windows Terminal, VS Code ou tout émulateur moderne, pour `--color` en 24 bits. |
|
|
164
|
+
|
|
165
|
+
FFmpeg livre les trois binaires ensemble. **Rouvre le terminal après l'installation**, pour que le
|
|
166
|
+
`PATH` soit rechargé :
|
|
167
|
+
|
|
168
|
+
```powershell
|
|
169
|
+
winget install Gyan.FFmpeg # Windows
|
|
170
|
+
brew install ffmpeg # macOS
|
|
171
|
+
sudo apt install ffmpeg # Debian / Ubuntu
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Depuis les sources
|
|
175
|
+
|
|
176
|
+
```powershell
|
|
177
|
+
git clone https://github.com/Josueagbeta0/hachure-ascii.git
|
|
178
|
+
cd hachure-ascii
|
|
179
|
+
py -m venv .venv
|
|
180
|
+
.venv\Scripts\Activate.ps1
|
|
181
|
+
python -m pip install -e ".[dev]"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
L'extra `dev` ajoute pytest. `-e` installe en mode éditable : le paquet pointe vers les sources, une
|
|
185
|
+
modification est prise en compte sans réinstaller.
|
|
186
|
+
|
|
187
|
+
### Pour que `hachure` réponde partout
|
|
188
|
+
|
|
189
|
+
Installé dans un environnement virtuel, `hachure` n'existe que quand cet environnement est activé.
|
|
190
|
+
Pour l'avoir dans n'importe quel terminal, installe-le dans ton Python principal, dont le dossier
|
|
191
|
+
`Scripts` est déjà dans le `PATH` :
|
|
192
|
+
|
|
193
|
+
```powershell
|
|
194
|
+
py -m pip install -e "C:\chemin\vers\hachure-ascii"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Si la commande reste introuvable, la forme module fonctionne toujours :
|
|
198
|
+
|
|
199
|
+
```powershell
|
|
200
|
+
python -m hachure
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Vérifier
|
|
204
|
+
|
|
205
|
+
```powershell
|
|
206
|
+
hachure doctor
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```text
|
|
210
|
+
hachure 0.3.0
|
|
211
|
+
python 3.11.9 (C:\...\Python311\python.exe)
|
|
212
|
+
numpy 1.26.4
|
|
213
|
+
PIL 12.1.1
|
|
214
|
+
ffmpeg ffmpeg version 9.0.1-full_build
|
|
215
|
+
ffplay ffplay version 9.0.1-full_build
|
|
216
|
+
ffprobe ffprobe version 9.0.1-full_build
|
|
217
|
+
terminal 120 x 40 cellules
|
|
218
|
+
profondeur truecolor
|
|
219
|
+
rapport cell. 0.5
|
|
220
|
+
tty stdout True
|
|
221
|
+
codec stdout utf-8
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Les cinq dernières lignes décrivent le terminal, pas le projet, et expliquent la plupart des
|
|
225
|
+
surprises de rendu :
|
|
226
|
+
|
|
227
|
+
| Ligne | Ce qu'elle contrôle |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `terminal` | La grille disponible. `--width` est un plafond, pas une cible : il y est ramené. |
|
|
230
|
+
| `profondeur` | `truecolor` = 24 bits. Si c'est `none`, `--color` ne produira rien de visible. |
|
|
231
|
+
| `rapport cell.` | Largeur d'une cellule divisée par sa hauteur. Monte vers `0.6` si l'image paraît étirée. |
|
|
232
|
+
| `codec stdout` | Doit être `utf-8` pour les glyphes demi-bloc. |
|
|
233
|
+
|
|
234
|
+
`tty stdout` et `profondeur` changent selon que la sortie va vers un vrai terminal ou vers un
|
|
235
|
+
fichier. Redirigée, la détection renvoie `False` et `none` : c'est attendu, pas une panne.
|
|
236
|
+
|
|
237
|
+
Un guide pas à pas — prérequis, vérification, premiers rendus, dépannage — est dans
|
|
238
|
+
[`docs/INSTALLATION.md`](docs/INSTALLATION.md), avec une version mise en page dans
|
|
239
|
+
[`docs/installation.html`](docs/installation.html).
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## En ligne de commande
|
|
244
|
+
|
|
245
|
+
```powershell
|
|
246
|
+
hachure # le menu interactif
|
|
247
|
+
hachure list # tous les moteurs disponibles
|
|
248
|
+
hachure doctor # diagnostic
|
|
249
|
+
|
|
250
|
+
hachure image photo.jpg --width 100 --color --edges --auto-levels
|
|
251
|
+
hachure video clip.mp4 --color --charset detailed --edges --auto-levels --fit cover --width 1000
|
|
252
|
+
hachure camera --color --edges
|
|
253
|
+
hachure demo blackhole
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`Ctrl+C` arrête proprement toute animation, vidéo ou caméra : le curseur et les couleurs du terminal
|
|
257
|
+
sont restaurés dans tous les cas, y compris après une erreur.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Tirer le meilleur du mode caractère
|
|
262
|
+
|
|
263
|
+
Le mode caractère est celui par défaut, et c'est tout l'intérêt du projet : l'image est faite de
|
|
264
|
+
caractères, et c'est ce qui produit l'effet. Trois réglages l'affinent sans jamais sortir de cette
|
|
265
|
+
contrainte.
|
|
266
|
+
|
|
267
|
+
### `--edges` — la plus grosse amélioration à elle seule
|
|
268
|
+
|
|
269
|
+
La luminosité seule jette la forme. Avec `--edges`, la source est échantillonnée au-dessus de la
|
|
270
|
+
résolution des cellules, l'orientation de contour dominante dans chaque cellule est mesurée par un
|
|
271
|
+
tenseur de structure, et un glyphe de ligne adapté remplace le caractère de luminosité :
|
|
272
|
+
|
|
273
|
+
| Direction du contour | Glyphe |
|
|
274
|
+
| --- | --- |
|
|
275
|
+
| horizontale | `-` |
|
|
276
|
+
| diagonale descendante | `\` |
|
|
277
|
+
| verticale | `\|` |
|
|
278
|
+
| diagonale montante | `/` |
|
|
279
|
+
|
|
280
|
+
Silhouettes, visages et contours sortent du bruit. Seules les cellules portant un contour fort *et*
|
|
281
|
+
cohérent sont remplacées : les zones plates gardent leur ton. `--edge-strength` (de 0 à 1, défaut
|
|
282
|
+
`0.5`) fixe la force requise — baisse-la pour plus de traits, monte-la pour moins.
|
|
283
|
+
|
|
284
|
+
Le tenseur de structure plutôt qu'un gradient moyenné, parce que des gradients opposés le long d'un
|
|
285
|
+
même contour s'annulent à la moyenne : exactement ce qui arrive dans une cellule à cheval sur une
|
|
286
|
+
ligne fine.
|
|
287
|
+
|
|
288
|
+
### `--auto-levels` et `--gamma`
|
|
289
|
+
|
|
290
|
+
La luminosité est projetée linéairement sur la rampe : une scène sombre n'atteint donc jamais les
|
|
291
|
+
caractères denses, ni une scène claire les caractères clairsemés. `--auto-levels` étire chaque image
|
|
292
|
+
sur toute la rampe, mesurée aux 2ᵉ et 98ᵉ percentiles pour que quelques pixels isolés ne définissent
|
|
293
|
+
pas la plage. Les niveaux glissent d'une image à l'autre, si bien que la lecture ne pulse pas quand
|
|
294
|
+
un élément lumineux traverse le plan.
|
|
295
|
+
|
|
296
|
+
`--gamma` remodèle les tons moyens par-dessus : au-dessus de `1` ça éclaircit, en dessous ça
|
|
297
|
+
assombrit. Sur une source en couleur, les deux s'appliquent comme un gain sur les trois canaux, ce
|
|
298
|
+
qui préserve la teinte.
|
|
299
|
+
|
|
300
|
+
### `--charset smooth`
|
|
301
|
+
|
|
302
|
+
Les rampes intégrées sont ordonnées à l'œil. `smooth` est **mesurée** : chaque glyphe ASCII
|
|
303
|
+
imprimable a été rendu puis noté sur sa couverture d'encre et sur la régularité de répartition de
|
|
304
|
+
cette encre, avant que la rampe ne soit choisie de façon à ce que ses pas soient régulièrement
|
|
305
|
+
espacés en couverture réelle. Les dégradés progressent proprement au lieu de vaciller entre des
|
|
306
|
+
caractères sosies. Les quatre glyphes de ligne sont volontairement exclus, pour rester sans
|
|
307
|
+
ambiguïté quand `--edges` est actif.
|
|
308
|
+
|
|
309
|
+
En calibrer une pour ta propre police :
|
|
310
|
+
|
|
311
|
+
```powershell
|
|
312
|
+
hachure calibrate --font "C:\Windows\Fonts\CascadiaMono.ttf" --length 12
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Tout mettre ensemble
|
|
316
|
+
|
|
317
|
+
```powershell
|
|
318
|
+
hachure video clip.mp4 --color --charset detailed --edges --auto-levels --width 1000
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Cellules demi-bloc
|
|
322
|
+
|
|
323
|
+
`--cells half`, ou son raccourci `--half`, tasse deux pixels empilés dans chaque cellule grâce au
|
|
324
|
+
demi-bloc supérieur : le premier plan peint le pixel du haut, l'arrière-plan celui du bas. Le
|
|
325
|
+
résultat approche la photographie, ce qui veut aussi dire qu'il cesse de ressembler à des
|
|
326
|
+
caractères — à utiliser quand la fidélité compte plus que l'effet. `--edges` ne s'applique pas,
|
|
327
|
+
puisque le glyphe est toujours le même.
|
|
328
|
+
|
|
329
|
+
### Remplir l'écran
|
|
330
|
+
|
|
331
|
+
Le rendu préserve le rapport d'image de la source : un plan en 16:9 réclame environ 3,5 colonnes par
|
|
332
|
+
ligne. Si le terminal a moins de lignes que ce rapport ne l'exige, la largeur est réduite et des
|
|
333
|
+
colonnes restent vides. Trois éléments pilotent cela :
|
|
334
|
+
|
|
335
|
+
- **`--width`** est un maximum, pas une cible. Il vaut `160` par défaut : sur un terminal large il
|
|
336
|
+
faut le monter, et `--width 1000` est ramené sans risque à la largeur réelle.
|
|
337
|
+
- **`--fit cover`** recadre la source à la forme du terminal au lieu de l'encadrer de bandes. C'est
|
|
338
|
+
ce qui rend exploitables les vidéos verticales de téléphone.
|
|
339
|
+
- **`--char-aspect`** indique la largeur d'une cellule par rapport à sa hauteur. `0.5` convient à la
|
|
340
|
+
plupart des polices ; monte vers `0.6` si l'image paraît étirée verticalement. La variable
|
|
341
|
+
d'environnement `HACHURE_CHAR_ASPECT` le règle globalement.
|
|
342
|
+
|
|
343
|
+
Redimensionner la fenêtre en cours de lecture est pris en charge : la grille est recalculée et le
|
|
344
|
+
flux reprend à la position courante.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Rendu d'image
|
|
349
|
+
|
|
350
|
+
```powershell
|
|
351
|
+
hachure image IMAGE [options]
|
|
352
|
+
|
|
353
|
+
hachure image photo.png --width 120
|
|
354
|
+
hachure image photo.png --width 120 --output rendu.txt
|
|
355
|
+
hachure image photo.png --width 1000 --fit cover --half --color
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
| Option | Rôle |
|
|
359
|
+
| --- | --- |
|
|
360
|
+
| `--width N` | Largeur de sortie maximale. Défaut : `100`. |
|
|
361
|
+
| `--height N` | Hauteur de sortie maximale, facultative. |
|
|
362
|
+
| `--fit MODE` | `contain` (bandes) ou `cover` (recadre pour remplir). Défaut : `contain`. |
|
|
363
|
+
| `--char-aspect N` | Largeur d'une cellule divisée par sa hauteur. Défaut : `0.5`. |
|
|
364
|
+
| `--cells MODE` | `char` ou `half`. Défaut : `char`. |
|
|
365
|
+
| `--half` | Raccourci de `--cells half`. |
|
|
366
|
+
| `--color` / `--no-color` | Active ou désactive la couleur. Les images sont monochromes par défaut. |
|
|
367
|
+
| `--color-depth NOM` | `auto`, `truecolor`, `ansi256` ou `none`. |
|
|
368
|
+
| `--quant N` | Pas de quantification des couleurs. Défaut : `4`. |
|
|
369
|
+
| `--edges` | Remplace le caractère de rampe par un glyphe de ligne sur les vrais contours. |
|
|
370
|
+
| `--edge-strength N` | Force requise d'un contour, de `0` à `1`. Défaut : `0.5`. |
|
|
371
|
+
| `--auto-levels` | Étire l'image sur toute la rampe. |
|
|
372
|
+
| `--gamma N` | Tons moyens ; au-dessus de `1` ça éclaircit. Défaut : `1.0`. |
|
|
373
|
+
| `--charset NOM` | `classic`, `detailed`, `letters` ou `smooth`. |
|
|
374
|
+
| `--invert` | Retourne la rampe sombre-vers-clair. |
|
|
375
|
+
| `-o`, `--output CHEMIN` | Écrit le texte rendu dans un fichier UTF-8. |
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Rendu vidéo
|
|
380
|
+
|
|
381
|
+
```powershell
|
|
382
|
+
hachure video VIDEO [options]
|
|
383
|
+
|
|
384
|
+
hachure video clip.mp4 --no-audio
|
|
385
|
+
hachure video clip.mp4 --color --charset detailed --edges --auto-levels --width 1000
|
|
386
|
+
hachure video clip.mp4 --start 30 --duration 10 --loop
|
|
387
|
+
hachure video clip.mp4 --color --smoothing 0.35
|
|
388
|
+
hachure video clip.mp4 --color --half --record renders\clip.mp4
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
| Option | Rôle |
|
|
392
|
+
| --- | --- |
|
|
393
|
+
| `--fps N` | Cadence de lecture visée. Défaut : `20`. |
|
|
394
|
+
| `--width N` / `--height N` | Taille de rendu maximale en caractères. Largeur : `160` par défaut. |
|
|
395
|
+
| `--fit MODE` | `contain` ou `cover`. Défaut : `contain`. |
|
|
396
|
+
| `--cells MODE` / `--half` | Géométrie de cellule, comme ci-dessus. |
|
|
397
|
+
| `--color` / `--no-color` | Sortie en couleur. La vidéo est monochrome par défaut. |
|
|
398
|
+
| `--color-depth NOM` | Court-circuite la détection de couleur du terminal. |
|
|
399
|
+
| `--quant N` | Pas de quantification des couleurs. Défaut : `4`. |
|
|
400
|
+
| `--edges`, `--edge-strength N` | Glyphes de contour, comme ci-dessus. |
|
|
401
|
+
| `--auto-levels`, `--gamma N` | Mise en forme tonale, comme ci-dessus. |
|
|
402
|
+
| `--smoothing N` | Fondu temporel de `0` à `1` ; `1` est net, plus bas laisse des traînées. |
|
|
403
|
+
| `--max-frame-skip N` | Images consécutives abandonnables pour rattraper le retard. Défaut : `5`. |
|
|
404
|
+
| `--start N` | Position de départ en secondes. |
|
|
405
|
+
| `--duration N` | Arrête après ce nombre de secondes. |
|
|
406
|
+
| `--loop` | Redémarre quand la vidéo se termine. |
|
|
407
|
+
| `--no-audio` | Ne lance pas FFplay. |
|
|
408
|
+
| `--audio-delay N` | Décale l'audio de -30 à +30 s ; une valeur positive le retarde. |
|
|
409
|
+
| `--record CHEMIN` | Écrit la sortie rendue en `.mp4` ou `.gif`. |
|
|
410
|
+
| `--charset NOM`, `--invert` | Rampe de caractères, comme ci-dessus. |
|
|
411
|
+
|
|
412
|
+
Les métadonnées de rotation sont respectées : un plan filmé en portrait au téléphone est mesuré et
|
|
413
|
+
rendu droit, plutôt qu'écrasé.
|
|
414
|
+
|
|
415
|
+
```text
|
|
416
|
+
┌─ FFmpeg → images brutes mises à l'échelle → NumPy → cellules → terminal
|
|
417
|
+
vidéo source ──────────┤
|
|
418
|
+
└─ FFplay → audio
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Le moteur suit un calendrier à l'horloge murale. Quand le rendu prend du retard, il abandonne un
|
|
422
|
+
nombre borné d'images décodées au lieu de laisser la dérive s'installer.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Capture caméra
|
|
427
|
+
|
|
428
|
+
```powershell
|
|
429
|
+
hachure camera --list
|
|
430
|
+
hachure camera --color --half
|
|
431
|
+
hachure camera --device "Integrated Webcam" --size 1280x720 --duration 10 --record me.gif
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
L'entrée caméra passe par DirectShow sous Windows, AVFoundation sous macOS et Video4Linux2 sous
|
|
435
|
+
Linux. L'audio n'est jamais capturé. Si une caméra listée refuse de s'ouvrir sous Windows, autorise
|
|
436
|
+
les applications de bureau dans *Paramètres → Confidentialité et sécurité → Caméra*, et ferme tout
|
|
437
|
+
ce qui l'utilise déjà.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Enregistrement
|
|
442
|
+
|
|
443
|
+
`--record CHEMIN` peint chaque image rendue avec une police à chasse fixe et redirige le résultat
|
|
444
|
+
vers FFmpeg. Les séquences d'échappement sont relues au cours de cette passe : les enregistrements
|
|
445
|
+
gardent donc leurs couleurs.
|
|
446
|
+
|
|
447
|
+
| Option | Rôle |
|
|
448
|
+
| --- | --- |
|
|
449
|
+
| `--record CHEMIN` | Destination ; `.gif` produit un GIF animé, tout le reste une vidéo H.264. |
|
|
450
|
+
| `--font CHEMIN` | `.ttf` à chasse fixe pour peindre. Une police système est trouvée automatiquement. |
|
|
451
|
+
| `--font-size N` | Corps de la police, qui fixe la résolution de sortie. Défaut : `16`. |
|
|
452
|
+
|
|
453
|
+
Disponible pour `video`, `camera` et `demo`.
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Démos procédurales
|
|
458
|
+
|
|
459
|
+
Cinq scènes calculées en temps réel, sans source externe : projection, éclairage et tampon de
|
|
460
|
+
profondeur écrits à la main.
|
|
461
|
+
|
|
462
|
+
```powershell
|
|
463
|
+
hachure demo NOM [options]
|
|
464
|
+
|
|
465
|
+
hachure demo donut --fps 30
|
|
466
|
+
hachure demo planet --width 120 --charset detailed
|
|
467
|
+
hachure demo blackhole --width 140 --record blackhole.gif
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
| Démo | Technique |
|
|
471
|
+
| --- | --- |
|
|
472
|
+
| `cube` | Rotation des sommets, projection en perspective, normales de face, élimination des faces arrière, remplissage de triangles, tampon de profondeur interpolé. |
|
|
473
|
+
| `sphere` | Reconstruction de la sphère cellule par cellule et éclairage directionnel. |
|
|
474
|
+
| `donut` | Échantillonnage d'un tore paramétrique, éclairage par les normales, perspective, tampon de profondeur. |
|
|
475
|
+
| `planet` | Sphère en rotation, relief procédural, face nocturne, halo atmosphérique. |
|
|
476
|
+
| `blackhole` | Disque d'accrétion en coordonnées polaires, étoiles déterministes, lueur asymétrique, effet d'anneau de photons. |
|
|
477
|
+
|
|
478
|
+
Toutes acceptent `--width`, `--height`, `--fps`, `--charset`, `--invert` et les options
|
|
479
|
+
d'enregistrement. Les dimensions sont réduites au besoin pour tenir dans le terminal, et la scène se
|
|
480
|
+
recompose quand la fenêtre est redimensionnée.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Couleurs
|
|
485
|
+
|
|
486
|
+
La profondeur de couleur est déduite de `COLORTERM`, de `TERM` et du terminal hôte, et `NO_COLOR`
|
|
487
|
+
est respecté. `--color-depth` court-circuite le résultat :
|
|
488
|
+
|
|
489
|
+
- `truecolor` — premier plan et arrière-plan sur 24 bits. Windows Terminal, VS Code, la plupart des
|
|
490
|
+
émulateurs modernes.
|
|
491
|
+
- `ansi256` — la palette xterm-256, pour les terminaux plus anciens.
|
|
492
|
+
- `none` — monochrome.
|
|
493
|
+
|
|
494
|
+
`--quant` aligne les valeurs de canal sur un pas avant leur émission. Des valeurs plus grandes
|
|
495
|
+
produisent de plus longues plages de couleur identique, donc moins de séquences d'échappement — ce
|
|
496
|
+
qui compte sur un terminal lent.
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Notes de performance
|
|
501
|
+
|
|
502
|
+
- Les séquences d'échappement sont construites depuis des préfixes en cache et émises **par plage**
|
|
503
|
+
plutôt que par cellule, et seules les lignes réellement modifiées sont repeintes.
|
|
504
|
+
- Monter `--quant`, baisser `--fps` ou baisser `--width` sont les leviers efficaces quand la lecture
|
|
505
|
+
saccade, dans cet ordre.
|
|
506
|
+
- Les demi-cellules doublent le nombre de pixels : elles coûtent environ deux fois plus par image
|
|
507
|
+
que les cellules `char`, à grille égale.
|
|
508
|
+
- `--edges` échantillonne trois pixels par côté de cellule, donc FFmpeg décode neuf fois plus de
|
|
509
|
+
pixels. Sur une grille de terminal normale cela reste faible, mais c'est la seule option qui
|
|
510
|
+
augmente le coût de **décodage** et pas seulement celui du rendu.
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Développement
|
|
515
|
+
|
|
516
|
+
```powershell
|
|
517
|
+
python -m unittest discover -s tests -v # suite complète, comme la CI
|
|
518
|
+
python -m pytest # équivalent
|
|
519
|
+
python -m pytest tests/test_render.py -k half
|
|
520
|
+
python -m unittest tests.test_render.RenduMonochromeTests
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
197 tests, moins d'une seconde. Aucun n'ouvre de vrai terminal ni ne lit de vrai fichier média : les
|
|
524
|
+
appels FFmpeg sont simulés, les touches du menu et les saisies clavier le sont aussi, et les rendus
|
|
525
|
+
sont comparés sur de petits tableaux NumPy construits à la main.
|
|
526
|
+
|
|
527
|
+
Ni linter ni formateur configuré. La CI lance la suite sur Python 3.10 et 3.14, puis vérifie que
|
|
528
|
+
`hachure --version` et `hachure list` répondent.
|
|
529
|
+
|
|
530
|
+
**Le code, les commentaires, la documentation et les noms de tests sont en français.** Deux
|
|
531
|
+
exceptions, marquées par un commentaire à leur emplacement : les sous-chaînes comparées à la sortie
|
|
532
|
+
de FFmpeg, et les messages internes d'argparse et d'unittest, qui passent par gettext sans catalogue
|
|
533
|
+
français dans CPython.
|
|
534
|
+
|
|
535
|
+
### Architecture
|
|
536
|
+
|
|
537
|
+
Tout converge vers un point de conversion unique. Deux familles de sources — pixels décodés,
|
|
538
|
+
géométrie procédurale — et trois sorties — terminal, fichier texte, vidéo enregistrée — se
|
|
539
|
+
rejoignent dans `render.py`.
|
|
540
|
+
|
|
541
|
+
```text
|
|
542
|
+
media/image.py (Pillow) ─┐
|
|
543
|
+
media/video.py (FFmpeg) ─┼→ tableau NumPy → tone.py → edges.py → render.py → texte ANSI ─┬→ terminal.py
|
|
544
|
+
renderers/*.py ──────────┘ (les démos émettent du texte directement) color.py └→ export.py
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
| Module | Rôle |
|
|
548
|
+
| --- | --- |
|
|
549
|
+
| `render.py` | Le pivot. `RenderStyle` porte rampe, mode de cellule, profondeur de couleur, quantification et contours. Ses `pixel_cols()`/`pixel_rows()` disent au décodeur *en amont* combien de pixels réclame une grille de caractères. La sortie est bâtie en plages compressées par ligne, jamais cellule par cellule — et une plage ne franchit jamais une frontière de ligne, pour que chaque ligne reste redessinable seule. |
|
|
550
|
+
| `edges.py` | Suréchantillonne 3× par côté de cellule et choisit `-`, `\`, `\|` ou `/` via un tenseur de structure. Les glyphes de contour sont rangés après la fin de la rampe, si bien que l'aval indexe un alphabet unique sans cas particulier. |
|
|
551
|
+
| `tone.py` | Niveaux automatiques et gamma. À état entre les images : `reset()` sur un saut ou un redémarrage de boucle. |
|
|
552
|
+
| `color.py` | Mémoïse les préfixes ANSI, indexés par RVB compacté. Détecte la profondeur via `NO_COLOR`, `COLORTERM`, `TERM`. |
|
|
553
|
+
| `terminal.py` | Dimensionnement et boucle d'animation. `Screen.draw()` ne repeint que les lignes modifiées. `terminal_session()` garantit la restauration du curseur et des couleurs. |
|
|
554
|
+
| `media/video.py` | Construit la commande FFmpeg, lit des images brutes de taille fixe, suit un calendrier à l'horloge murale. L'audio est un processus FFplay distinct. |
|
|
555
|
+
| `renderers/` | Démos autonomes enregistrées dans un dictionnaire `DEMOS`. Ajouter une démo = un module plus une entrée. |
|
|
556
|
+
| `export.py` | Réanalyse les codes ANSI reçus et les repeint avec une police à chasse fixe, vers FFmpeg. |
|
|
557
|
+
| `menu.py` | Le menu. Ne duplique aucune option : il compose une liste d'arguments et la passe à `main()`. |
|
|
558
|
+
| `cli.py` | Argparse seulement. Les groupes d'options sont partagés entre sous-commandes. |
|
|
559
|
+
|
|
560
|
+
NumPy et Pillow sont importés paresseusement, et les fonctions manipulant des tableaux reçoivent
|
|
561
|
+
`np` en paramètre explicite plutôt que de l'importer au niveau du module.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Crédits
|
|
566
|
+
|
|
567
|
+
`hachure` dérive de [ASCII-Art](https://github.com/RipperdocNiladri/ASCII-Art), publié sous licence
|
|
568
|
+
MIT par **Niladri Pal** et **Talal Alqahs**. Le rendu, la CLI et le menu ont été réécrits depuis,
|
|
569
|
+
mais la notice de copyright d'origine est conservée dans [`LICENSE`](LICENSE), comme la licence MIT
|
|
570
|
+
l'exige.
|
|
571
|
+
|
|
572
|
+
Distribué sous licence MIT.
|