Auteur : Sylvain B.

Projets

Un ensemble de Julia sur la calculatrice et avec…

Pour ce dernier projet de cette année, j’ai décidé de faire, le tout sur la calculatrice Numworks, un ensemble de Julia en utilisant des palettes de couleurs, cela donnera une image bien plus belle que le script Mandelbrot de la calculatrice par exemple.

D’où vient l’idée ?

J’ai déjà réalisé des projets sur les fractales, mais surtout sur les ensemble de Mandelbrot. Cette fois, j’ai voulu faire un ensemble de Julia, la « mère » des ensemble de Mandelbrot. Mais je voulais pousser la technique un peu plus loin.

Pour rendre mes fractales encore plus belles, j’ai voulu créer une palette de couleur ce qui rendra les dégradés de couleurs bien plus beaux.

Qu’est-ce qu’un ensemble de Julia ?

Avant d’expliquer comment faire cette palette, qu’est-ce qu’un ensemble de Julia ?

Un ensemble de Julia, c’est une fractale qui peut avoir plusieurs formes, ici, j’utilise l’ensemble ayant pour valeur du nombre complexe c : 0,36 + 0,36i ce qui nous donne cela quand on met 100 itérations maximum :

Comment ajouter et rendre les couleurs de ma fractale plus belle ainsi que les problèmes rencontrés ?

Avant de commencer à regarder les modifications, il faut comprendre la différence entre les couleurs de mon premier script et les couleurs de la palette. Dans le premier script, ma couleur prenait le nombre d’itérations pour se définir (voir si elle est rouge, bleu, vert…). La palette, elle, regarde à quel point le pixel est loin ou près de la fractale pour choisir la teinte et la couleur du pixel.

Pour créer une fractale de l’ensemble de Julia en utilisant des palettes de couleurs, j’ai procédé comme cela :

  • Tout d’abord à partir du script Mandelbrot de la Numworks et grâce à des recherches, je crée un script Julia qui fabrique un ensemble de Julia avec c = 0,36 + 0,36J. Cela donne une image comme ci-dessus. les lignes :
z = complex(0,0)
c = complex(xmin+*x/319-2.5, -2.5*y/221+1.25)

Deviennent :

z = complex(xmin+(xmax-xmin)*x/320+(ymax-(ymax-ymin)*y/222)*1J)
c = complex(0.36,0.36)
  • Ensuite, je crée dans mon script deux boucles for j in range qui vont me fabriquer une liste « palette » de 255 listes ayant 3 valeurs chacune qui représente les valeurs RGB :
palette = []
r = 255
g = 255
b = 255
for j in range(0,128):
  b = 255 -2 * j
  palette.append([r,g,b])
for j in range(128,256):
  r = 255 -2 * (j-128)
  g = 255 -2 * (j-128)
  palette.append([r,g,b])
  • Explication : Pour modifier la couleur, je crée au début de ma fonction JULIA 3 variables, R pour le rouge, G pour le vert et B pour le bleu qui ont chacune comme valeur initiale 255. Pour réaliser la fractale, je rentre dans le range() les valeurs 0 et 128 pour faire une coupure entre quand je transforme le blanc en bleu et quand je transforme le bleu en noir. Je modifie les variables R et G en faisant 255 – 2 * j ce qui va diminuer les deux valeurs, puis je les rajoute dans une liste de ma palette qui contient mes valeurs R, G et B à chaque nouvelles valeurs de j dans une autre boucle for qui va de 128 à 255 je modifie la valeur du bleu avec l’équation 255 – 2 * (j – 128) et je rajoute à nouveau une liste avec les valeurs r g et b.
  • Je modifie la définition de ma variable « col » pour qu’elle récupère les trois valeurs contenues dans l’une des listes de ma palette et je rajoute une variable couleur pour rendre plus simple le code :
couleur = palette[int(255*i/N_iteration)]
col = color(couleur[0],couleur[1],couleur[2])
  • Et voilà, quand je lance mon programme avec 100 itérations, cela me donne cette fractale :

Code complet :

from kandinsky import*
def Julia(N_iteration):
    palette = []
    xmax = 2
    xmin = -2
    ymax = 1.3875
    ymin = -1.387
    r = 255
    g = 255
    b = 255
    for j in range(0,128):
        b = 255 -2 * j
        palette.append([r,g,b])
    for j in range(128,256):
        r = 255 -2 * (j-128)
        g = 255 -2 * (j-128)
        palette.append([r,g,b])
    for x in range(320):
        for y in range(222):
            i = 0
            z = complex(xmin+(xmax-xmin)*x/320+(ymax-(ymax-ymin)*y/222)*1J)
            c = complex(0.36,0.36)
            while i < N_iteration and abs(z) < 2:
                i = i + 1
                z = z*z+c
            couleur = palette[int(255*i/N_iteration)]
            col = color(couleur[0],couleur[1],couleur[2])
            set_pixel(x,y,col)

Télécharger le .py

Autres modifications possibles :

  • Tout d’abord la couleur, pour l’instant, je n’ai seulement réussi à faire les 6 couleurs des synthèses additive et soustractive (rouge, vert, bleu, magenta, cyan et jaune). Je m’y suis pris trop tard pour faire les autres couleurs, car il faut modifier les équations des lignes qui modifie les variables R, G et B. Pour faire cela il faut modifier le code comme ceci :
for j in range(0,128):
        b = 255 -2 * j
        palette.append([r,g,b])
    for j in range(128,256):
        r = 255 -2 * (j-128)
        g = 255 -2 * (j-128)
        palette.append([r,g,b])
  • On peut modifier le point de vue en modifiant les valeurs de ces lignes :
xmax = 2
xmin = -2
ymax = 1.3875
ymin = -1.387

Il faut faire attention aux proportions sinon l’image sera distordue, par exemple si on met ces valeurs, on obtient :

xmax = 0.5
xmin = -0.5
ymax = 0.346875
ymin = -0.346875

Ici, j’ai seulement zoomé au milieu de l’image

  • Et on peut aussi faire comme sur l’image ci-dessous en passant par plusieurs couleurs en faisant plusieurs boucles for j in range :

Ici, on passe du gris, au jaune, au rouge, puis au noir.

En conclusion :

Même si ce travail ne m’a pas permis d’apprendre de nouvelles choses en python, il m’a fait travailler mon raisonnement pour programmer de futurs codes. Je me suis bien amusé à le faire, car je l’ai fait avec mon père qui avait déjà fait la même chose, mais dans d’autres langages de programmation comme RUST, et vu qu’il n’est pas très fort en python, même si il a la réflexion pour le code, nous avons pu croiser nos compétences pour finir le programme. Le seul bémol, la résolution de la calculatrice est tellement faible que cela nous donne une image très pixelisée.

Sources :

Tutoriels

Comprendre et s’amuser avec le script « Mandelbrot » de la…

Les fractales sont des figures mathématiques qui présentent des formes fragmentées et répétitives lorsqu’on les observe de manière de plus en plus précise. Elles peuvent prendre des formes similaires à des flocons de neige ou à des éponges.

C’est quoi une fractale / un ensemble de Mandelbrot ?

Pour analyser le code, il est important de comprendre ce qu’est une fractale de Mandelbrot. Cette figure mathématique a été nommée ainsi en l’honneur de Benoît Mandelbrot, qui en a popularisé les représentations dans les années 1980.


Les fractales de Mandelbrot sont basées sur l’ensemble des points c du plan complexe pour lesquels la suite de nombres complexes suivante est définie par récurrence :

Cet ensemble de points a été découvert par Gaston Julia et Pierre Fatou avant la première guerre mondiale, et permet de déterminer les ensembles de Julia associés à la suite. Chaque point du plan complexe correspond à un ensemble de Julia distinct. Les points de l’ensemble de Mandelbrot correspondent précisément aux ensembles de Julia connexes (c’est-à-dire formant une seule partie), tandis que ceux en dehors correspondent aux ensembles de Julia non connexes. L’ensemble de Mandelbrot est étroitement lié aux ensembles de Julia, et ils produisent des formes similairement complexes. (Wikipedia)

Pour en apprendre plus sur l’histoire et le fonctionnement de la géométrie fractale et des fractales de Mandelbrot le documentaire les couleurs de l’infini écrit par Arthur C. Clarke, sorti en 1995, disponible sur Netflix explique très bien, bien que la qualité d’image soit un peu dépassée.

Pour dessiner la fractale de Mandelbrot, on va associer à chaque point du plan un nombre complexe. On insère ce nombre dans la formule ci-dessus, puis le résultat de la formule est à nouveau mis dans la formule et ainsi de suite. Au bout d’un moment le résultat soit grandit de plus en plus vers l’infini, soit le résultat ne grandit plus. Selon si le résultat va vers l’infini ou ne grandit plus, on va colorier le point d’une couleur ou d’une autre. Pour ne pas calculer jusqu’à l’infini, on fixe un nombre maximum de fois où on fait le calcul, le nombre d’iterations. Quand on atteind ce nombre d’itérations, si le résultat de la formule est inférieur à deux, on considère que le point fait partie de l’ensemble de Mandelbrot.

Explication du script de la Numworks :

Dans notre calculatrice Numworks, il y a un script nommé « Mandelbrot », il sert, quand on lui donne un nombre d’itérations (répétition d’une opération sur une valeur de départ), à « dessiner » avec Kandinsky une fractale Mandelbrot. Mais on peut le modifier pour faire en sorte qu’il affiche une fractale différente :

Par exemple faire un flocon de neige beaucoup trop complexe bien que plus beau qu’un flocon de koch selon moi.

Analyse du script ligne par ligne :

# This script draws a Mandelbrot fractal set
# N_iteration: degree of precision
import kandinsky
def mandelbrot(N_iteration):
  for x in range(320):
    for y in range(222):
# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i)
      z = complex(0,0)
# Rescale to fit the drawing screen 320x222
      c = complex(3.5*x/319-2.5, -2.5*y/221+1.25)
      i = 0
      while (i < N_iteration) and abs(z) < 2:
        i = i + 1
        z = z*z+c
# Choose the color of the dot from the Mandelbrot sequence
      rgb = int(255*i/N_iteration)
      col = kandinsky.color(int(rgb*0.81),int(rgb*0.13),int(rgb*0.18))
# Draw a pixel colored in 'col' at position (x,y)
      kandinsky.set_pixel(x,y,col)

On va expliquer le script sans prêter attention au commentaire pour les anglophobes :

  • Ligne 3 : on importe le module Kandinsky qui sera utile pour dessiner pixel par pixel l’ensemble de Mandelbrot
  • Ligne 4 : on définit la fonction avec l’argument N_iteration qui représente ici le degré de précision du dessin, plus il est grand plus la fractale sera précise.
  • Ligne 5 et 6 : on crée deux boucles « for » qui vont faire en sorte de « peindre » sur tout le format de l’écran
  • Ligne 8 : on définit la variable z par un nombre complexe qui est ici (0,0)
  • Ligne 10 : on crée la variable c qui prend comme valeur un nombre complexe qui correspond au format de l’écran
  • Ligne 11 : on crée la variable i, on lui donne la valeur 0
  • Ligne 12 : on commence à peindre le Mandelbrot à partir d’une boucle while qui se répète tant que i est plus petit que l’argument N_iteration et que la valeur absolue de z est plus petite que 2
  • Ligne 13 : on incrémente 1 a i pour que la boucle ne soit pas infinie
  • Ligne 14 : on calcule la suite qui définit le Mandelbrot : z*z+c
  • Ligne 16 : on crée la variable rgb qui est l’integer de 255 fois i divisé par N_iteration
  • Ligne 17 : on crée la variable col qui choisit la couleur générale de la fractale
  • Ligne 19 : on pose le pixel à l’abscisse x l’ordonnée y la couleur col

Explication des modifications possibles et simples du script :

La couleur :

Maintenant que l’on a compris le script, on peut commencer par modifier très simplement la couleur du Mandelbrot. On ne peut pas vraiment changer le fond, mais la couleur de la fractale. Par défaut, elle est orange-jaune, mais peut être changée si on modifie les valeurs de la ligne 17 au niveau des endroits où il y a marqué rgb*une_valeur.

Si on modifie cette valeur, on va modifier la couleur du Mandelbrot. Il y a trois endroits avec rgb*une_valeur qui définissent donc le code RGB de la fractale, la première fois que l’on a l’opération, elle représente le rouge puis le vert puis le bleu. Ainsi avec ceci en tête, on peut faire une fractale de la couleur que l’on veut. On peut sinon essayer de saturer les couleurs par exemple en mettant des valeurs supérieures à 1.

Ici par exemple en mettant 98 à chaque valeur on obtient cela :

La forme :

Si on veut modifier la forme, la tâche n’est pas beaucoup plus dure. Il faut modifier le calcul de la suite à la ligne 14, par exemple de base, on a z*z+c, mais on peut y mettre ce que l’on veut, par exemple z**42+c le plus important est de garder au moins un c et un z, car sinon la calculatrice n’affichera que le font.

Et voila quelques exemple de forme et de couleur jolies :

La fractale parfaite :

  • Ligne 14 :
z = z**42+c
  • Ligne 17 :
 col = kandinsky.color(int(rgb*0.95),int(rgb*0.2),int(rgb*0.2))

Et voila le résultat :

Le flocon de neige :

  • Ligne 14 :
:z = z**7+c
  • Ligne 17 :
 col = kandinsky.color(int(rgb*0.75),int(rgb*0.75),int(rgb*0.98))

Et voilà le résultat :

Et voilà, je trouve ça mieux qu’un flocon de Koch personnellement. Maintenant libre à vous de créer votre fractale !

Explication possible mais complexe de modification du script :

En fin d’année scolaire 2022, M. Robert, professeur de mathématiques de seconde nous a proposé de faire un devoir maison nommé « les mathématiques sont belles« , le but : créer de la beauté via des maths sous forme de fonction ou de script python en turtle. Personnellement, j’avais découvert le script Mandelbrot de la calculatrice et je voulais le réutiliser, j’ai donc modifié sa couleur (et pas sa forme, ça m’aurait valu un 20, mais bon) et surtout, j’ai modifié la prise de vue de la fractale :

Pour réaliser cette fractale, j’ai dû être aidé, car de base en tant qu’élève de seconde, je ne connaissais pas grand choses au nombre complexe, mon père m’a donc aidé et nous avons réussi à modifier la prise de vue du Mandelbrot.

Tout d’abord, on rajoute avant les deux boucles 4 variables qui vont delimiter le cadre de la vue que l’on veut. Mais il faut faire attention à incrémenter des valeurs qui feront un cadre proportionnel au format de l’écran de la calculatrice. Ci-dessous les valeurs que j’ai personnellement utilisées :

  • Xmax = -0.635
  • Xmin = -1.905
  • Ymax =  0.491
  • Ymin = 0.488

Ensuite On modifie la ligne 10 on l’écrit comme cela :

c = complex(Xmin+(Xmax-Xmin)*x/319,Ymin+(Ymax-Ymin)*y/221)

Et voilà, pour l’instant avec les valeurs inscrites dans les quatre nouvelles variables, on a juste zoomé légèrement sur l’avant du Mandelbrot, mais vous pouvez modifier à souhait les valeurs pour voir le Mandelbrot sous d’autres angles. Pour rechercher les coordonnées que vous voulez, vous pouvez rechercher des « Mandelbrot explorer » comme sur le site Science démos.

Conclusion :

En conclusion, avec tout ce qui vous est proposé dans ce tutoriel, vous pouvez modifier : la couleur, la forme et la prise de vue de votre fractale. À vous de créer votre œuvre d’art !

Art

Astronomie : L’amas de trous noirs

« L’amas de trous noirs » est le nom de notre premier projet développé en Python. Notre petit programme vous permettra de profiter d’un magnifique ciel étoilé… avec quelques trous noirs. En effet, ce programme générera pour vous une image d’une beauté à couper le souffle… Enfin, nous espérons !

Introduction

Représenter ce que l’être humain ne peut atteindre est une de ses passions favorites. En effet, nous allons vous présenter notre projet Python se prénommant : L’amas de trous noirs. Effrayant, n’est-ce pas ?

Le résultat final doit ressembler à ceci, mais vous le savez déjà :

Cette image est un des multiples résultats de notre code. Nous y reviendrons plus tard.

Comme vous pouvez le voir, il y a plusieurs éléments sur cette image : des trous noirs, des étoiles et un arrière-plan noir. Commençons par le programme pour former les trous noirs.

Nous avons utilisé le module turtle de python qui permet de dessiner à l’écran. Dans la suite de l’article, nous ferons référence à ce module sous le nom de tortue.

Les trous noirs

Un code non optimisé

C’est la première chose sur laquelle nous avons travaillé. Afin que vous conserviez votre santé mental, nous éviterons de vous montrer le code qui forme cette image :

Si vous le voulez vraiment :

def cercle(rayon, x, y, r=1, v=1, b=1):
    pensize(5)
    while rayon > 372 and r < 254:
        penup()
        goto(x, y - rayon)
        pendown()
        pencolor((r, v, b))
        circle(rayon)
        rayon -= 1
        r += 2
    while rayon > 244 and v < 254:
        penup()
        goto(x, y - rayon)
        pendown()
        pencolor((r, v, b))
        circle(rayon)
        rayon -= 1
        v += 2
    while rayon > 119 and b < 254:
        penup()
        goto(x, y - rayon)
        pendown()
        pencolor((r, v, b))
        circle(rayon)
        rayon -= 1
        b += 2
    while r > 0 and v > 0 and b > 0:
        penup()
        goto(x, y - rayon)
        pendown()
        pensize(2)
        pencolor((r, v, b))
        circle(rayon)
        rayon -= 1
        b -= 16
        v -= 16
        r -= 16


cercle(500, 0, 0)

Ce code ne nous satisfait pas car il a des limites d’utilisations. C’est-à-dire qu’il est impossible de dessiner des trous noirs de n’importe quel diamètre. On a par exemple ce cas où l’on choisi un rayon de 200 :

Ici, on constate que le dégradé n’est pas celui que l’on souhaitait.

Maintenant que nous avons vu un code qui vous pique encore les yeux, nous allons voir une autre version plus optimisée et bien plus flexible.

Un code optimisé

Avant toute chose, nous définissons au début de notre programme colormode(255) qui nous permettra de définir des couleurs au format (r, v, b), r pour rouge, v pour vert et b pour bleu qui sont les trois teintes permettant de composer n’importe quelle couleur.

Ensuite nous définissons notre fonction trou_noir(). Nous allons la découper en plusieurs portions de code afin de vous l’expliquer étape par étape.

  • Première étape : Initialisation de diverses informations.
colormode(255)

def trou_noir(x, y, rayon):
    color(0, 0, 0)
    pensize(5)
    r = -1
    v = -1
    b = -1
    penup()
    goto(x, y - rayon - 20)
    pendown()

Notre fonction aura donc besoin de trois paramètres : les coordonnées x, y et le rayon du trou noir. Nous indiquons que la couleur du stylo sera noir, en raison de l’arrière-plan qui sera noir également. Nous définissons le taille du trait à 5, qui est la valeur la plus basse tout en évitant des artéfacts au niveau du trou noir comme ceci :

Nous définissons trois variables qui, comme leur nom l’indique, seront les variables liées au changement de couleur. Nous les définissons avec une valeur négative, ce qui peut paraitre assez étrange, car les valeurs minimales pour le vert, le rouge ou le bleu sont zéro. Nous vous expliquerons un peu plus bas pourquoi nous faisons cela.

La fonction penup() permet de lever le stylo, la fonction goto() permet de le déplacer. On met en paramètre les coordonnées x et y en paramètre. La fonction pendown() permet elle, de poser le stylo.

Concernant la fonction goto(), elle prend en paramètre les coordonnées x et y, qui représente un point de l’écran. La tortue n’est capable de dessiner un cercle qu’à partir de sa base (le point le plus bas du cercle). Nous souhaitons utiliser le centre géométrique d’un cercle pour les positionner. Nous allons donc décaler la coordonnée y de la distance du rayon. Cela donne le code suivant : goto(x, y - rayon).

Nous avons choisi d’ajouter autour de notre trou noir une zone noire (🥴) de 20 pixels d’épaisseur (cette valeur a été défini de façon arbitraire). Nous verrons par la suite que c’est un choix esthétique, cette marge de 20 pixels se retrouve donc dans l’appel de notre fonction goto() : goto(x, y - rayon - 20).

  • Deuxième étape : Création d’un disque noir.
	begin_fill()
    fillcolor("black")
    circle(rayon + 20)
    end_fill()

Ce disque a été rajouté afin d’améliorer la fonction trou_noir() : en effet, après une première version de la fonction trou noir, nous avons amélioré le rendu graphique en dessinant un premier disque noir sur lequel la tortue dessine le trou noir. Cela apporte deux avantages : produire la marge de 20 pixels autour du trou noir (cf. paragraphe au-dessus) et remplir le centre de notre trou noir. Voyez ainsi :

Ce disque noir sert de transition entre le ciel étoilé (que vous verrons par la suite) et le trou noir en lui-même.

  • Troisième étape : Construction du trou noir (dégradé du rouge vers le blanc).
    while r < 255:
        penup()
        goto(x, y - rayon)
        pendown()
        r += 2
        pencolor((r, 0, 0))
        circle(rayon)
        rayon -= rayon/500

    while v < 255:
        penup()
        goto(x, y - rayon)
        pendown()
        v += 2
        pencolor((r, v, 0))
        circle(rayon)
        rayon -= rayon/500

    while b < 255:
        penup()
        goto(x, y - rayon)
        pendown()
        b += 2
        pencolor((r, v, b))
        circle(rayon)
        rayon -= rayon/500

Ces trois boucles while sont très similaires. Elles servent à créer le dégradé du trou noir. Ce que notre tortue fait est de dessiner un cercle d’une couleur différente à chaque fois que la boucle se répète. La boucle se termine une fois que la variable de la couleur donnée dans la condition while a atteint son maximum, soit 255. A chaque itération, nous incrémentons de 2 chaque variable de couleur, et nous le faisons juste avant de dessiner le cercle. Avec cette incrémentation, il nous faudrait 128 itérations pour arriver à la valeur 256. Cette valeur est donc supérieur à 255, nous avons donc décidé d’initialiser chaque variable de couleur à -1 afin de compenser ce problème. Pour la variable r par exemple, la première fois que notre boucle va s’exécuter, elle va s’incrémenter de 2 ( -1 + 2 = 1 ), puis choisir une couleur pour le stylo : pencolor((r, 0, 0)), puis dessiner le cercle. Lors de la dernière itération, r est égal à 253, donc 253 + 2 = 255, la tortue dessine le cercle avec cette valeur. Nous sortirons ensuite de la boucle. Nous faisons ceci trois fois, pour les trois variables de couleurs.

La dernière ligne qui est intéressant dans l’extrait cité est rayon -= rayon/500. La valeur 500 provient de notre « programme d’essai », celui non optimisé. Nous avons utilisé la proportionnalité par rapport à la valeur trouvée grâce à ce programme. Dans le programme d’essai nous retirions 1 au rayon pour un cercle de rayon 500. Ici nous retirons au rayon le résultat de la division du rayon par 500. Par exemple si le rayon est 500, on retirera 1 ( 500/500 = 1 ), si c’est 250, on retirera 0,5 ( 250/500 = 0,5 ), si c’est 750, on retirera 1,5 ( 750/500 = 1,5 ), etc. Ceci permet de faire des trous noir de la taille que l’on souhaite.

  • Quatrième étape : Dégradé du blanc au noir de finition.
    while r > 0 and v > 0 and b > 0:
        penup()
        goto(x, y - rayon)
        pendown()
        pencolor((r, v, b))
        circle(rayon)
        rayon -= rayon / 500
        b -= 16
        v -= 16
        r -= 16
    pencolor("black")
    circle(rayon)

Cette dernière boucle permet de faire un fort dégradé entre le blanc et le centre du trou noir, qui est… noir (🥴). Elle n’a rien de particulier par rapport à ce que l’on a expliqué plus haut mis à part la valeur forte de la réduction des valeurs des trois variables de couleurs (que l’on décrémente de 16) et également la condition de la boucle qui permet qu’elle s’arrête une fois que la couleur atteinte est le noir.

Les deux dernières lignes de l’extrait permettent d’éviter ce problème :

Les étoiles

Voici le code pour former les étoiles :

def etoile(x, y, branche, longueur, couleur=(255, 230, 0)):
    penup()
    goto(x, y)
    degre_angle = 180 - (360 / (branche * 2))
    begin_fill()
    fillcolor(couleur)
    for i in range(branche):
        forward(longueur)
        right(degre_angle)
    end_fill()

Cette fonction prend en paramètre les coordonnées x et y, le nombre de branche, la longueur des branches (plus précisément la longueur des segments constituants l’étoile) et la couleur (avec une couleur par défaut qui est un jaune-orange qui tend plus vers le jaune). On lève notre stylo, on se positionne de manière à ce que le centre de l’étoile soit les coordonnées données en paramètres et… pas de pendown() ? En effet celui-ci serait inutile pour former les étoiles car nous n’allons pas utiliser la fonction de tracé de ligne mais plutôt utiliser la fonction de remplissage (de couleur) de zone. Ensuite, nous créons une variable degre_angle. Elle est très importante car elle va définir le degré des angles qui vont permettre de former l’étoile. Regardez :

L’étoile est formée de la même manière que l’extrait cité sauf qu’ici le stylo est baissé, et il n’y a pas de couleur de remplissage. Les angles qui nous intéressent sont au bout des branches. Ce sont ces angles là que le programme contrôle.

En A), notre tortue avance. Elle est à une position avec un angle de 0°. L’angle en rouge est égal à 180°. C’est notre 180 dans la formule ! Et (360 / (branche * 2)) est en fait l’angle intérieur. Pour savoir la valeur de cet angle, il faut imaginer l’étoile dans un cercle. La somme de tous les sommets de cette étoile dans un cercle donne 360°. Et dans une étoile, le nombre de sommet est le double du nombre de branche.

Ensuite en B), elle s’oriente de l’angle calculé dans la variable degre_angle. Le trait violet est un trait de construction rajouté pour que le schéma soit plus clair.

Pour finir en C), la tortue avance. Ici on voit bien l’angle formé qui est celui de degre_angle. Le trait en cyan est un trait de construction.

D’une pierre deux coup, nous vous avons expliqué aussi la boucle qui est dans le programme. Avec begin_fill(), fillcolor(couleur) et end_fill() nous remplissons l’intérieur de l’étoile avec la couleur donnée en paramètre.

Le ciel (arrière-plan et étoiles)

Cool ! On a bien avancé. On continue avec cette fois-ci un bon bgcolor("black") qui est censé faire un arrière-plan en noir et… non. Cela ne va pas marcher, nous verrons cela un peu après, une fois le code entièrement présenté. Nous allons donc concevoir une fonction arriere_plan() :

def arriere_plan():
    # .ps est incapable de récupérer le bgcolor() selon internet...
    # Et des barres blanches apparaissent avec ce code dans le .png généré...
    penup()
    goto(-640, 0)
    pendown()
    pensize(900)
    forward(1280)

Il y a un bavard dans le code de ce que je vois, mais il a raison (ou on a tous les deux torts, c’est une éventualité). Encore une fois, nous verrons ceci un peu plus tard. Cette fonction est très simple, elle conçoit simplement un fond noir, en se positionnant le plus à gauche du canvas (zone de dessin) et en traçant un trait de la taille du canvas (le résultat doit avoir une taille de 1280 pixels de largeur par 720 pixels d’hauteur) avec une taille exagérée.

def ciel():
    arriere_plan()
    liste_couleur = [(255, 255, 255), (255, 230, 0)]
    for i in range(0, randint(71, 121)):
        liste_couleur[1] = (255, randint(150, 230), 0)
        etoile(randint(-630, 630), randint(-350, 350), 5, 10, choice(liste_couleur))
    for i in range(0, randint(16, 31)):
        liste_couleur[1] = (255, randint(150, 230), 0)
        etoile(randint(-620, 620), randint(-340, 340), 7, 20, choice(liste_couleur))
    for i in range(0, randint(6, 16)):
        liste_couleur[1] = (255, randint(150, 230), 0)
        etoile(randint(-600, 600), randint(-320, 320), 15, 40, choice(liste_couleur))

Occupons nous maintenant de la fonction ciel(). Nous appelons la fonction arriere_plan() puis le programme dessine des étoiles dans notre ciel. Nous utilisons simplement trois boucles qui dessineront 3 types d’étoiles différentes. En effet grâce aux paramètres branche et longueur, nous pouvons former des étoiles complètements différentes, diversifiant le ciel. Concernant le paramètre couleur, nous avons une liste qui varie à chaque fois que la boucle se répète. C’est plus précisément le second élément qui varie. On modifie aléatoirement la quantité de vert dans la couleur, rendant l’étoile plus ou moins orangée. La fonction choice() permet de choisir un élément aléatoirement parmi une liste donnée en paramètre. Pour finir sur ce bout de code, chaque boucle for génère un nombre aléatoire d’étoile entre deux valeurs précisées (ex : randint(71, 121)). On remarque que les étoiles plus petites apparaissent plus nombreuses.

Voici un rendu de la fonction ciel() :

Taille du canvas : 2560×1080 pixels
Taille du canvas : 1280×720 pixels

On remarque bien le fond généré « artificiellement » dans la première image, et comment cela rend dans les bonnes dimensions.

Assemblement et problèmes

Bien, nous avons notre ciel, plus qu’à rajouter les trous noirs. Nous en ferons trois :

ciel()
trou_noir(randint(-490, -250), randint(-260, 260), randint(50, 150))
trou_noir(randint(100, 250), randint(-260, 260), randint(50, 150))
trou_noir(randint(400, 490), randint(-260, 260), randint(50, 150))

On appelle donc notre fonction ciel() et nous générons trois trous noirs. Ils sont positionnés aléatoirement mais pour éviter une fusion de trou noir, nous les plaçons dans des coordonnées où ils ne peuvent s’entrechoquer. On remarque qu’il y a un décalage de 150 entre chaque plage de coordonnées x. C’est dû à la taille qui est aléatoire entre 50 et 150.

Une fusion de trou noir… C’est très beau, vous ne trouvez pas ?

Nous vous avions proposé un résultat en début d’article, en voici un autre :

Le rendu turtle
Le fichier PNG généré

On remarque diverse imperfection. La plupart provienne d’un problème lors de la conception du fichier postscript et nous n’avons pas trouver comment régler ce problème.

Un autre problème que nous avons eu a été celui-là :

Effectivement, je n’ai plus l’impression de voir un ciel…

Bon, qu’est-ce qui s’est passé ? Lorsque nous avons généré cette image, nous utilisions la fonction bgcolor("black") pour générer le fond noir sauf qu’après une petite recherche sur internet, le fichier postscript ne prend pas en compte cette action, il ne la « voit » pas. Nous avons donc dû changer par la fonction arriere_plan() que nous vous avons expliqué plus haut.

Conclusion

Vous pouvez former des images complètement différentes de ce que l’on a généré simplement en modifiant quelques valeurs ou en rajoutant une boucle for… Vous pouvez faire un ciel beaucoup plus dense, des dégradés plus profonds, des trous noirs plus grands, etc. Nous avons voulu faire un programme qui puisse avec très peu de modification générer des résultats d’une grande diversité.

Télécharger le .py

Si l’envie vous prend de rendre ces personnalisations plus simples d’accès et/ou optimiser notre code, voici un petit cadeau :

L’image finale