Go to file
2026-09-12 18:49:46 +02:00
__pycache__ Optimisation de l'ordre des layer générés 2026-09-04 14:32:07 +02:00
tests/data Version initiale 2026-09-04 13:52:44 +02:00
.gitignore Ne plus versionner le cache 2026-09-12 18:49:46 +02:00
circle_packing.inx Possibilité d'utiliser un motif 2026-09-12 18:48:05 +02:00
circle_packing.py Possibilité d'utiliser un motif 2026-09-12 18:48:05 +02:00
deploy.ps1 Version initiale 2026-09-04 13:52:44 +02:00
packing_core.py Possibilité d'utiliser un motif 2026-09-12 18:48:05 +02:00
README.md Possibilité d'utiliser un motif 2026-09-12 18:48:05 +02:00
test_packing.py Possibilité d'utiliser un motif 2026-09-12 18:48:05 +02:00

Remplissage par cercles (circle packing) — extension Inkscape

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é.

Table of content

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é « Circle packing » ; 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
  • 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 :

.\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 et packing_core.py 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 trois fichiers doivent rester ensemble, circle_packing.py important packing_core.py.

Utilisation

  1. Sélectionner une ou plusieurs formes fermées.
  2. Extensions → AlexDesign → Remplissage par cercles (circle packing)…
  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.

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

circle_packing.inx     Description de la boîte de dialogue Inkscape
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
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 :

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 : 24 passés, 3 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 :

pip install lxml tinycss2 cssselect2 cssselect
$env:PYTHONPATH = 'C:\Program Files\Inkscape\share\inkscape\extensions'
python -m pytest test_packing.py -q     # 27 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é.

Aucun outil de lint n'est configuré dans le projet ; le code suit PEP 8.