235 lines
11 KiB
Markdown
235 lines
11 KiB
Markdown
# Remplissage par cercles (circle packing) — extension Inkscape
|
|
|
|
<table>
|
|
<tr>
|
|
<td width="50%" valign="top">
|
|
<img src="docs/rendu.png" alt="Rectangle, cercle, chemin courbe et cadre troué remplis de cercles non chevauchants de tailles variées, chacun dans la couleur de sa forme source" width="420">
|
|
</td>
|
|
<td valign="top">
|
|
|
|
Extension Inkscape 1.x qui remplit la surface d'une forme fermée avec des cercles
|
|
non chevauchants, selon la technique du *greedy random circle packing* : des points
|
|
sont tirés au hasard dans la forme, et sur chacun on place le plus grand cercle
|
|
possible qui ne déborde pas du contour et ne touche aucun cercle déjà posé.
|
|
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
## Table of content
|
|
|
|
- [Fonctionnalités](#fonctionnalités)
|
|
- [Installation](#installation)
|
|
- [Utilisation](#utilisation)
|
|
- [Paramètres](#paramètres)
|
|
- [Structure du projet](#structure-du-projet)
|
|
- [Développement](#développement)
|
|
|
|
## Fonctionnalités
|
|
|
|
- Remplissage de `path`, `rect`, `circle`, `ellipse`, `polygon` (les groupes
|
|
sélectionnés sont explorés pour en extraire les formes ; `line` et `polyline`
|
|
sont ignorées)
|
|
- Forme déposée au choix : cercle (par défaut) ou **forme personnalisée** — le motif
|
|
est le dernier objet sélectionné, chaque copie étant mise à l'échelle pour
|
|
tenir dans le disque calculé, orientée au hasard et stylée comme le motif
|
|
- Gestion des trous : les sous-chemins internes sont exclus (règle pair-impair)
|
|
- Rayons minimal et maximal réglables, écart entre cercles (*gap*) et marge
|
|
intérieure au contour, exprimés dans l'unité de votre choix
|
|
- Les cercles héritent du style (fond, contour) de la forme source
|
|
- Résultat placé dans un groupe nommé « Remplissage par cercles » ; forme d'origine
|
|
conservée par défaut
|
|
- Transformations respectées : le groupe généré annule celle de son parent
|
|
- Graine aléatoire pour un résultat reproductible
|
|
- Cercles ordonnés de proche en proche (chaîne du plus proche voisin) : les logiciels
|
|
de découpe laser qui suivent l'ordre du document minimisent leurs déplacements à vide
|
|
- Grille de hachage spatial : des dizaines de milliers de tentatives restent rapides
|
|
- Interface traduite en français et en anglais, selon la langue d'Inkscape
|
|
(anglais pour toute autre langue)
|
|
- Script de déploiement PowerShell (installation, mise à jour, désinstallation)
|
|
|
|
## Installation
|
|
|
|
Aucune dépendance externe : l'extension utilise `inkex`, fourni avec Inkscape.
|
|
|
|
### Windows — script de déploiement
|
|
|
|
Inkscape fermé, depuis le dossier du projet :
|
|
|
|
```powershell
|
|
.\deploy.ps1 # copie vers %APPDATA%\inkscape\extensions\circlePacking
|
|
.\deploy.ps1 -Uninstall # retire le sous-dossier de l'extension
|
|
.\deploy.ps1 -Force # passe outre le contrôle « Inkscape ouvert »
|
|
.\deploy.ps1 -FolderName 'mes-cercles' -ExtensionsDir 'D:\autre\chemin'
|
|
```
|
|
|
|
Le script crée le sous-dossier, remplace les fichiers existants, nettoie le
|
|
`__pycache__` obsolète et supprime les traces d'une ancienne installation posée
|
|
à plat à la racine des extensions. Redémarrer Inkscape ensuite.
|
|
|
|
### Installation manuelle
|
|
|
|
Copier `circle_packing.inx`, `circle_packing.py`, `packing_core.py`,
|
|
`images/parameters_en.png` et le dossier `locale/` dans un sous-dossier (par
|
|
exemple `circlePacking`) du dossier des extensions utilisateur, puis redémarrer
|
|
Inkscape :
|
|
|
|
- Windows : `%APPDATA%\inkscape\extensions`
|
|
- Linux : `~/.config/inkscape/extensions`
|
|
- macOS : `~/Library/Application Support/org.inkscape.Inkscape/config/inkscape/extensions`
|
|
|
|
Inkscape explore récursivement ce dossier ; les fichiers doivent rester
|
|
ensemble : `circle_packing.py` importe `packing_core.py`, l'onglet « Aide »
|
|
affiche `images/parameters_en.png` et `locale/` porte les traductions.
|
|
|
|
## Utilisation
|
|
|
|
1. Sélectionner une ou plusieurs formes fermées.
|
|
2. **Extensions → AlexDesign → Remplissage par cercles (circle packing)…**
|
|
(*Circle Packing Fill…* en anglais)
|
|
3. Régler les paramètres, puis **Appliquer**.
|
|
|
|
### Remplir avec une forme personnalisée
|
|
|
|
Une extension Inkscape n'a pas accès au presse-papiers : la forme copiée doit
|
|
d'abord être collée dans le document.
|
|
|
|
1. Coller la forme voulue (**Ctrl+V**).
|
|
2. Sélectionner les formes à remplir, puis ajouter le motif à la sélection
|
|
**en dernier** (**Maj+clic**).
|
|
3. Lancer l'extension, choisir « Forme déposée : Forme personnalisée ».
|
|
|
|
Le dernier objet sélectionné sert de motif et n'est pas rempli ; il reste
|
|
en place même si « Conserver la forme d'origine » est décoché. Chaque copie est
|
|
mise à l'échelle pour que son cercle circonscrit vaille le rayon calculé — la
|
|
garantie de non-chevauchement et de non-débordement est donc conservée — puis
|
|
orientée au hasard (reproductible à graine fixée) et stylée comme le motif.
|
|
Les motifs très allongés donnent un remplissage plus lâche, leur cercle
|
|
circonscrit étant large devant leur surface.
|
|
|
|
Le fichier `tests/data/shapes.svg` contient quatre cas d'essai : rectangle, cercle,
|
|
chemin courbe et chemin avec trou. L'image en tête de ce README en est le rendu
|
|
(graine 7, rayons de 0,6 à 12 mm, écart et marge de 0,4 mm, 40 000 tentatives,
|
|
forme d'origine supprimée).
|
|
|
|
Conseils de réglage :
|
|
|
|
- densité insuffisante → augmenter le nombre de tentatives, réduire le rayon
|
|
minimal, l'écart ou la marge
|
|
- calcul trop long → réduire le nombre de tentatives ou plafonner le nombre de
|
|
cercles
|
|
- aucun cercle placé → un message l'indique ; le rayon minimal + la marge dépassent
|
|
probablement la largeur de la forme
|
|
|
|
## Paramètres
|
|
|
|

|
|
|
|
| Paramètre | Défaut | Rôle |
|
|
|---|---|---|
|
|
| Forme déposée | Cercle | Cercle, ou forme personnalisée (dernier objet sélectionné) |
|
|
| Rayon minimal | 1.0 | En dessous, le cercle candidat est rejeté |
|
|
| Rayon maximal | 20.0 | Taille maximale d'un cercle |
|
|
| Écart entre cercles | 0.5 | Distance imposée entre deux cercles voisins |
|
|
| Marge intérieure au contour | 0.5 | Distance imposée entre un cercle et le bord |
|
|
| Unité | mm | Unité des quatre longueurs ci-dessus (mm, cm, px, pt, in) |
|
|
| Nombre de tentatives | 20000 | Plus il est élevé, plus le remplissage est dense (et lent) |
|
|
| Nombre maximal de cercles | 5000 | Garde-fou ; 0 = illimité |
|
|
| Graine aléatoire | 0 | 0 = aléatoire ; toute autre valeur rend le résultat reproductible |
|
|
| Finesse d'échantillonnage | 0.2 | Précision de l'aplatissement des courbes de Bézier |
|
|
| Conserver la forme d'origine | oui | Sinon la forme source est supprimée |
|
|
|
|
Le remplissage greedy atteint typiquement 60 à 75 % de couverture.
|
|
|
|
## Structure du projet
|
|
|
|
```text
|
|
circle_packing.inx Boîte de dialogue Inkscape (textes source en anglais)
|
|
circle_packing.py Extension : lecture de la sélection, aplatissement des
|
|
chemins, écriture des <circle> dans le SVG
|
|
packing_core.py Noyau géométrique sans dépendance à inkex
|
|
i18n.py Chaîne de traduction (extract / update / compile)
|
|
po/ Modèle circlepacking.pot et catalogues en.po, fr.po
|
|
locale/ Catalogues compilés (.mo), déployés
|
|
images/parameters_en.png Schéma de l'onglet « Aide » (déployé)
|
|
docs/schema_parametres.py Générateur du schéma (fr pour le README, en pour le dialogue)
|
|
docs/parametres.png Schéma des paramètres du README (non déployé)
|
|
docs/rendu.png Image de présentation du README (non déployée)
|
|
deploy.ps1 Déploiement / désinstallation sous Windows
|
|
test_packing.py Tests pytest
|
|
tests/data/shapes.svg Formes d'exemple
|
|
```
|
|
|
|
API de `packing_core.py` (utilisable seule, hors Inkscape) :
|
|
|
|
| Fonction | Rôle |
|
|
|---|---|
|
|
| `rings_bbox(rings)` | Boîte englobante d'un contour |
|
|
| `bounding_circle(rings)` | Centre de la bbox et rayon circonscrit — sert à inscrire un motif dans le disque calculé |
|
|
| `point_in_rings(x, y, rings)` | Point dans la forme, règle pair-impair |
|
|
| `distance_point_segment(px, py, x1, y1, x2, y2)` | Distance point → segment |
|
|
| `distance_to_rings(x, y, rings)` | Distance au contour le plus proche |
|
|
| `SpatialGrid(cell_size)` | Grille de hachage des cercles posés |
|
|
| `order_by_proximity(circles)` | Réordonne en chaîne du plus proche voisin (départ au coin haut-gauche) |
|
|
| `pack_circles(rings, min_radius, max_radius, …)` | Boucle de packing, renvoie `[(x, y, r), …]` déjà ordonnés |
|
|
|
|
Un contour (`rings`) est une liste d'anneaux, chaque anneau étant une liste de
|
|
points `(x, y)` fermée implicitement.
|
|
|
|
## Développement
|
|
|
|
Le noyau `packing_core.py` ne dépend pas d'Inkscape et se teste avec un Python
|
|
standard :
|
|
|
|
```powershell
|
|
python -m venv .venv
|
|
.venv\Scripts\activate # Linux/macOS : source .venv/bin/activate
|
|
pip install pytest
|
|
python -m pytest test_packing.py -q
|
|
```
|
|
|
|
Résultat attendu : **28 passés, 4 ignorés**. Les tests ignorés sont ceux de bout en
|
|
bout, sautés quand `inkex` n'est pas importable. Pour les exécuter, ajouter le dossier
|
|
des extensions d'Inkscape au `PYTHONPATH` et installer les dépendances d'`inkex`
|
|
absentes d'un Python standard :
|
|
|
|
```powershell
|
|
pip install lxml tinycss2 cssselect2 cssselect
|
|
$env:PYTHONPATH = 'C:\Program Files\Inkscape\share\inkscape\extensions'
|
|
python -m pytest test_packing.py -q # 32 passés
|
|
```
|
|
|
|
Les tests couvrent l'appartenance à une forme trouée, les distances au contour, la
|
|
grille spatiale, et les invariants du packing : aucun chevauchement, rayons dans
|
|
les bornes, cercles tous intérieurs, reproductibilité à graine fixée. L'ordre de sortie est
|
|
comparé à une recherche du plus proche voisin en force brute et à la longueur du parcours
|
|
d'un ordre mélangé. S'y ajoutent les traductions (catalogues complets et recompilés) et la
|
|
cohérence entre le `.inx`, les arguments du script et les images de la boîte de dialogue.
|
|
|
|
### Traductions
|
|
|
|
Les textes source (`.inx` et appels `_()` de `circle_packing.py`) sont en anglais ;
|
|
`i18n.py` les extrait et compile les catalogues, sans dépendance à gettext :
|
|
|
|
```powershell
|
|
python i18n.py # extract + update + compile ; liste les msgstr manquants
|
|
```
|
|
|
|
Après modification d'un texte : lancer `python i18n.py`, compléter les `msgstr`
|
|
vides de `po/fr.po`, relancer. `po/en.po` se remplit tout seul ; ne jamais éditer
|
|
les `.mo`. Pour ajouter une langue, l'ajouter à `LANGUAGES` dans `i18n.py`.
|
|
|
|
### Schéma des paramètres
|
|
|
|
```powershell
|
|
python docs/schema_parametres.py # fr : docs/parametres.png (1400 px)
|
|
# en : images/parameters_en.png (900 px)
|
|
```
|
|
|
|
Les cercles du schéma sont calculés par `packing_core`, le dessin reste donc fidèle
|
|
au résultat réel. La version anglaise sert à l'onglet « Aide » de la boîte de
|
|
dialogue (Inkscape ne traduit pas le chemin d'une image). L'export PNG passe par
|
|
`inkscape.com`.
|
|
|
|
Aucun outil de lint n'est configuré dans le projet ; le code suit PEP 8.
|