Logiciel libre de géométrie, d'analyse et de simulation multiplateforme par Yves Biton

Accueil Tutoriels l’API de MathGraph32

Programmer une figure en Python : La fougère de Barnsley

modification jeudi 25 avril 2024.


Depuis la version 7.9.1, MathGraph32 permet de créer des objets dans une figure en langage Python.

Ici nous allons générer une fougère de Barnsley. Ce sera l’occasion d’utiliser le calcul matriciel de MathGraph32 en Python.

Si vous changez le code Python ci-dessous, cliquez sur le bouton Exécuter en dessous de ce cadre d’édition.

Voir au-dessous de la figure des explications sur le code Python employé et l’utilisation de l’API de MathGraph32

Explications concernant le code Python ci-dessus

Lignes 2 à 4 : On crée des calculs MathGraph32 nommés p1, p2 et p3. Ces calculs seront utilisés dans la fonction barnsley.
Lignes 5 à 12 : on crée des matrices MathGraph32 qui seront utilisées dans la fonction barnsley.
Exemple :
A1 = addMatrix(’A1’, [1]) : cette ligne crée une matrice MathGraph32 nommée A1 à deux lignes et deux colonnes. La première ligne est formée des nombres 0 et 0, la deuxième ligne est formée des nombres 0 et 0,16.
La boucle for des lignes 30 à 32 appelle 500 fois la fonction barnsley en remplçant à chque fois la variable pos par ce qui a été retourné par pos lors de l’appel précédent. Les résultats de ces appels sont ajoutés à la liste res.
Une fois cette boucle exécutée, la liste res contient donc des listes de deux éléments contenant les coordonnées des points calculés.
Une fois cette liste créée, les lignes 33 à 35 créent les 5000 points dont les coordonnées dans le repère de la figure sont données par es éléments de la liste res.

Examinons maintenant la fonction barnsley :

def barnsley(coord):
   X = addMatrix('X', [[coord[0]], [coord[1]]])
   alea = addCalc('alea', 'rand(0)')
   cas = addCalc('cas', 'si(alea<p1,1,si(alea<p1+p2,2,si(alea<p1+p2+p3,3,4)))')
   A = addCalcMat('A', 'si(cas=1,A1,si(cas=2,A2,si(cas=3,A3,A4)))')
   B = addCalcMat('B', 'si(cas=1,B1,si(cas=2,B2,si(cas=3,B3,B4)))')
   Y = addCalcMat('Y', 'A*X+B')
   valY = getMatValue(Y) # Renvoie une liste représentant une matrice colonne
   vala = valY[0][0] # Première colonne de la première ligne
   valb = valY[1][0] # Première colonne de la deuxième ligne
   deleteObj(alea)
   deleteObj(X)
   return [vala, valb]

Le paramètre coord est à chaque appel une liste de deux éléments contenant les coordonnées du point précédent.
Ligne alea = addCalc(’alea’, ’rand(0)’) : On crée dans la figure MathGraph32 un calcul de nom alea et don la formule est rand(0) qui renvoie un nombre aléatoire compris en 0 et 1 (1 exclu).
Attention : la variable alea qui contient le calcul créé est une variable locale à la fonction. Quand on sort de la fonction cette variable Python est détruite, mais la figure MathGraph32 continue de contenir un calcul nommé nommé alea. Si nous ne détruisions pas ce calcul à la fin de la fonction, lors de l’appel suivant, nous aurions une erreur car il est impossible de rajouter dans MathGraph32 un calcul ayant le même nom qu’un calcul déjà créé. C’est ce qui est fait par la ligne deleteOj(alea) qui détruit ce calcul et tous les objets qui en dépendent (donc aussi cas, A, B et Y).
La ligne A = addCalcMat(’A’, ’si(cas=1,A1,si(cas=2,A2,si(cas=3,A3,A4)))’) crée une matrice A avec une formule conditionnelle spécifique à MathGraph32.

Voici ci-dessous du code qui prend beaucoup plus de temps à être exécuté et nous allons expliquer pourquoi :

p1 = addCalc('p1', '0.01')
p2 = addCalc('p2', '0.85')
p3 = addCalc('p3', '0.07')
A1 = addMatrix('A1', [[0, 0], [0, 0.16]])
A2 = addMatrix('A2', [[0.85, 0.04], [-0.04, 0.85]])
A3 = addMatrix('A3', [[0.2, -0.26], [0.23, 0.22]])
A4 = addMatrix('A4', [[-0.15, 0.28], [.260, 0.24]])
B1 = addMatrix('B1', [[0], [0]])
B2 = addMatrix('B2', [[0], [1.6]])
B3 = addMatrix('B3', [[0], [1.6]])
B2 = addMatrix('B4', [[0], [0.44]])

def barnsley(coord):
   X = addMatrix('X', [[coord[0]], [coord[1]]])
   alea = addCalc('alea', 'rand(0)')
   cas = addCalc('cas', f'si(alea<p1,1,si(alea<p1+p2,2,si(alea<p1+p2+p3,3,4)))')
   A = addCalcMat('A', f'si(cas=1,A1,si(cas=2,A2,si(cas=3,A3,A4)))')
   B = addCalcMat('B', f'si(cas=1,B1,si(cas=2,B2,si(cas=3,B3,B4)))')
   Y = addCalcMat('Y', 'A*X+B')
   valY = getMatValue(Y) # Renvoie une liste représentant une matrice colonne
   vala = valY[0][0] # Première colonne de la première ligne
   valb = valY[1][0] # Première colonne de la deuxième ligne
   addPointXY(vala, valb, '', 'blue', '.')
   deleteObj(X)
   deleteObj(alea)
   return [vala, valb]

pos = [0, 0]
for i in range(2000):
   pos = barnsley(pos)

Copiez ce code dans le presse-papier et collez le dans la fenêtre d’édition du code Python après avoir vidé son contenu.
Bien que le nombre de points à calculer soit passé de 5000 à 2000 le rendu de la figure prend plus de temps (et cela empire en augmentant le nombre de points calculés).
Voilà l’explication : Imaginons qu’on entre dans la fonction barnsley pour i = 1000 (donc après l’avoir déjà exécutée 1000 fois). Comme la ligne addPointXY(vala, valb, ’’, ’blue’, ’.’) a déjà été excécutée 1000 fois, le figure contient déjà 1000 points.
Quand les lignes suivantes vont être exécutées :

   deleteObj(X)
   deleteObj(alea)

par exemple deleteObj(alea) recherchera parmi tous les objets de la figure quels sont ceux qui dépendent de alea et ces objets sont d’autant plus nombreux qu’on a déjà rajoutés plus de points à la figure.
D’où l’intérêt pour la rapidité de la figure de ne créer les points qu’à la fin comme dans l’exemple initiale du code ci-dessous :

p1 = addCalc('p1', '0.01')
p2 = addCalc('p2', '0.85')
p3 = addCalc('p3', '0.07')
A1 = addMatrix('A1', [[0, 0], [0, 0.16]])
A2 = addMatrix('A2', [[0.85, 0.04], [-0.04, 0.85]])
A3 = addMatrix('A3', [[0.2, -0.26], [0.23, 0.22]])
A4 = addMatrix('A4', [[-0.15, 0.28], [.260, 0.24]])
B1 = addMatrix('B1', [[0], [0]])
B2 = addMatrix('B2', [[0], [1.6]])
B3 = addMatrix('B3', [[0], [1.6]])
B2 = addMatrix('B4', [[0], [0.44]])

def barnsley(coord):
   X = addMatrix('X', [[coord[0]], [coord[1]]])
   alea = addCalc('alea', 'rand(0)')
   cas = addCalc('cas', f'si(alea<p1,1,si(alea<p1+p2,2,si(alea<p1+p2+p3,3,4)))')
   A = addCalcMat('A', f'si(cas=1,A1,si(cas=2,A2,si(cas=3,A3,A4)))')
   B = addCalcMat('B', f'si(cas=1,B1,si(cas=2,B2,si(cas=3,B3,B4)))')
   Y = addCalcMat('Y', 'A*X+B')
   valY = getMatValue(Y) # Renvoie une liste représentant une matrice colonne
   vala = valY[0][0] # Première colonne de la première ligne
   valb = valY[1][0] # Première colonne de la deuxième ligne
   deleteObj(alea)
   deleteObj(X)
   return [vala, valb]

pos = [0, 0]
res = [pos]
for i in range(5000):
   pos = barnsley(pos)
   res.append(pos)
for i in range(len(res)):
   pos = res[i]
   addPointXY(pos[0], pos[1], '', 'blue', '.')

[10, 0], [0, 0.16