Étiquette : Python

Art

Le grand canyon

Pour ce premier projet en spécialité NSI, nous avons décidé de produire un paysage inspiré du Grand Canyon. Dans cet article, vous pourrait retrouver toutes les étapes pour créer cette image.

Le projet

Pour ce projet nous avons donc décidé de produire une image d’un paysage inspirée du Grand Canyon. Pour cela nous utilisons le module turtle, le module utilisé pour produire l’image ainsi que le module random. Nous avons également utilisé le script permettant d’exporter une image générée par turtle en .png que vous pouvez retrouver ici.

Au commencement

Nous nous sommes tout d’abord inspirés de l’image ci-dessous, et après en avoir réalisé un rapide croquis à la main nous avons commencé notre script : nous avons créé plusieurs fonctions pour chaque élément, que nous avons ensuite appelées dans un ordre précis à la fin du code. 

Structure du script

Pour la structure de notre script, nous avons choisi de réaliser seulement des fonctions (sol, soleil, cactus, montagnes…) et de les appeler à la fin dans un ordre respectif, en y mettant les coordonnées pour celles qui le demande, afin de construire notre image.

Le script étape par étape

Commençons par l’appel des modules et la mise en place de turtle.

from turtle import *
from random import randint

"""
Ce fichier est expliqué ici : https://nsi.xyz/py2png
Un autre tutoriel permet de commencer : https://nsi.xyz/tutoriels/decouvrir-le-module-turtle-de-python/
"""

try:
    from PIL import Image
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False
titre = "Paysage - construite avec turtle"
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")
setup(1280, 720)
colormode(255)
speed(0)
hideturtle()

Ce code permet d’appeler les différents modules mais également de vérifier que l’utilisateur a bien installé le module PIL et dans le cas contraire un message d’erreur s’affichera et lui donnera un lien pour installer le module et tout ça sans que le script ne s’arrête.  Aprés vérification du bon fonctionnement du script, on met à jour le titre du projet ainsi que sa taille. Enfin on définit le type de couleurs utilisées (R,G,B), la vitesse de la tortue, et on la dissimule.

Commençons par la première fonction : le fond

def fond():
penup()
rciel = 0
gciel = 0
bciel = 0
hauteur = -360
goto(-642,-358)
pendown()
while hauteur != 360:
pencolor(round(189 + rciel), round(224 + gciel), round(254 + bciel))
forward(1280)
hauteur += 1
goto(-640, hauteur)
rciel += (-42/180)
gciel += (-7/45)
bciel += (-15/500)

Pour le fond on aurait pu utiliser une fonction qui crée directement un rectangle et qui le remplit avec fill_rect, or celui-ci aurait été uni alors que nous souhaitons observé un léger dégradé. Nous avons donc produit un script qui, en faisant avancé la tortue à partir d’un point de départ goto et qui ajoute la différence de chaque couleur (rouge, vert et bleu) entre la couleur de début du dégradé et la couleur de fin, produit un fond dégradé, ici de couleur bleue.

Deuxième, troisième et quatrième fonction : Les sols

def sol1():
	penup()
	goto(-640,-360)
	fillcolor(65, 72, 51)
	begin_fill()
	forward(1280)
	left(90)
	forward(175)
	left(90)
	forward(1280)
	left(90)
	forward(175)
	left(90)
	end_fill()

Pour réaliser un effet de profondeur nous avons réalisé un dégradé au niveau des sols en créant trois fonctions différentes sous la forme de la première fonction : sol1(). Pour réaliser le sol on a utilisé de nouveau goto qui permet de placer la tortue avant de la faire avancer pour remplir l’espace.

On répète donc cette fonction en changeant les coordonnées de goto et en éclaircissant la couleur.

def sol2():
	penup()
	goto(-640,-186)
	fillcolor(101, 109, 74)
	begin_fill()
	forward(1280)
	left(90)
	forward(50)
	left(90)
	forward(1280)
	left(90)
	forward(50)
	left(90)

def sol3():
	penup()
	goto(-640,-137)
	fillcolor(164, 172, 134)
	begin_fill()
	forward(1280)
	left(90)
	forward(50)
	left(90)
	forward(1280)
	left(90)
	forward(50)
	left(90)
	end_fill()

Ensuite on réalise les montagnes, pour cela on doit créer différentes fonctions, car aucune montagne n’a la même forme.

Montagne 1 :

def montagne1():
    #montagne derrière la première
    penup()
    goto(-560,-190)
    fillcolor(158, 78, 39)
    begin_fill()
    forward(250)
    left(120)
    forward(50)
    right(30)
    forward(160)
    left(90)
    forward(10)
    left(90)
    forward(50)
    right(90)
    forward(5)
    right(90)
    forward(80)
    left(90)
    forward(140)
    left(90)
    forward(40)
    right(90)
    forward(8)
    left(90)
    forward(120)
    right(42)
    forward(70)
    end_fill()

On utilise donc de nouveau goto pour faire déplacer notre tortue et faire en sorte qu’elle dessine a peu près la même forme de montagne que sur la photo. On remplit à l’aide de begin_fill() que l’on arrête à la fin avec end_fill().

On crée ensuite les autres montagnes en changeant la forme et en les éclaircissant légèrement pour créer un effet de profondeur.

Montagne 2 et 3 :

def montagne2():
    #montagne derrière la première
    penup()
    goto(340,-150)
    fillcolor(173, 94, 55)
    begin_fill()
    right(228)
    forward(210)
    left(120)
    forward(30)
    right(30)
    forward(130)
    left(90)
    forward(140)
    left(90)
    forward(9)
    right(80)
    forward(10)
    left(78)
    forward(120)
    right(40)
    forward(2)
    end_fill()
    
def montagne3():
    #montagne derrière la première
    penup()
    goto(-60,-100)
    fillcolor(201, 116, 75)
    begin_fill()
    left(132)
    forward(125)
    left(120)
    forward(50)
    right(30)
    forward(75)
    left(90)
    forward(15)
    right(45)
    forward(7)
    left(45)
    forward(25)
    left(45)
    forward(7)
    right(45)
    forward(26)
    left(90)
    forward(26)
    right(90)
    forward(5)
    right(90)
    forward(10)
    left(90)
    forward(5)
    left(90)
    forward(50)
    right(50)
    forward(5)
    end_fill()

Ensuite la fonction permettant de dessiner les cactus : On a d’abord attribué les coordonnées x et y à la fonction cactus, qui seront choisies lors de son appel,  déterminant alors l’emplacement du cactus sur l’écran, et permettant de l’appeler plusieurs fois a différent coordonnées. On définit ensuite la couleur, la taille et l’épaisseur du trait, on oriente la tortue vers le bas avec la fonction setheading()à 270, puis on commence à dessiner les bras et le tronc du cactus.

def cactus(x, y) :
    pencolor(56, 176, 0)
    pensize(10)
    penup()
    goto(x, y)
    pendown()
    setheading(270)
    forward(50)
    left(90)
    forward(30)
    left(90)
    forward(50)
    penup()
    goto(x + 16, y + 16)
    pendown()
    setheading(270)
    forward(125)

On réalise une autre fonction qui dessine un cactus comme le code précédent mais avec des dimensions différentes.

def cactus_petit(x, y) :
    pencolor(56, 176, 0)
    pensize(5)
    penup()
    goto(x, y)
    pendown()
    setheading(270)
    forward(25)
    left(90)
    forward(15)
    left(90)
    forward(25)
    penup()
    goto(x + 7, y + 7)
    pendown()
    setheading(270)
    forward(65)

Pour dessiner les virevoltants, que l’on peut retrouver dans le désert : on a tout d’abord attribue les coordonnées x et y (comme précédemment) à la fonction paille, pour l’appeler à différents endroits de l’image. Nous avons ensuite crée une boucle « for i in range » qui dessine des cercles beiges et qui incrémente 1 à chaque nouveau cercle dessiné, réalisant donc, à chaque répétition, des cercles de plus en plus grands.

def paille(x, y) :
    penup()
    goto(x,y)
    pendown()
    radius = 0
    for i in range(20) :
        color(166, 138, 100)
        pensize(1)
        circle(radius)
        radius += 1
       

Par la suite nous codons la fonction dessinant les nuages : On a (encore une fois) attribue les coordonnées x et y à la fonction nuage, pour l’appeler à différents endroits de l’image ; pour que les deux nuages soient tous les deux horizontaux, on utilise la fonction setheading() en l’orientant à 0 et on dessine ensuite 3 cercles blancs de différentes tailles, chacun d’eux avançant après avoir été dessiné.

def nuage(x, y):
    penup()
    goto(x, y)
    setheading(0)
    fillcolor(255, 255, 255)
    begin_fill()
    circle(25)
    forward(40)
    circle(35)
    forward(35)
    circle(18)
    end_fill()

Puis celle pour dessiner le soleil : on a tout simplement établit l’emplacement de celui-ci et fait un cercle de couleur jaune.

def soleil() :
    penup()
    goto(385,250)
    fillcolor(224, 161, 66)
    begin_fill()
    circle(30)
    end_fill()

Enfin, la fonction pour dessiner un oiseau simple en bâton : on a établit la couleur et l’épaisseur du trait, orienté la tortue avec la fonction setheading() à 290 pour qu’elle soit bien horizontale, puis fait une boucle qui trace un trait et qui tourne a gauche à deux reprises.

def oiseaux() :
    penup()
    pencolor(0, 0, 0)
    pensize(3)
    goto(395, 258)
    pendown()
    setheading(290)
    for i in range(2):
        left(50)
        forward(15)
       

Pour produire notre image finale, nous appelons toutes les fonctions (en ajoutant les coordonnées de certaines) :

#appel des fonctions        
fond(), sol1(), sol2(), sol3(), montagne1(), montagne2(), montagne3()  
cactus(-600,-150), cactus(500,-240)
cactus_petit(230, -100)
paille(100, -200), paille(-550, -300), paille(480, -330)
nuage(-480, 200), nuage(490, 180)
soleil()
oiseaux()

Résultat final

Télécharger en .py

Projets

La Patrouille de France

Pour ce premier projet de NSI, nous avons décider de prendre comme thème une image de la Patrouille de France, vous pourrez retrouver ici les démarches par lesquelles nous sommes passées.

Notre vidéo :

Le Fond :

Tout d’abord pour obtenir notre image nous avons créer un fond dégradé bleu :

n = 350
s = (0, 0, 139)  
e = (173, 216, 230)  
w, h = 1280, 720


def degrade(st, end, step, t_step):
    return st + (end - st) * step / t_step

Pour obtenir ce résultat nous avons donc créer les paramètres de notre dégradé, il y en a 4 : le nombre de ligne que va faire la tortue (n) ; la couleur de départ (s) ; la couleur de fin (e) ; La longueur et la largeur (w, h) . Afin que la tortue change petit à petit de couleur, on créait la fonction degrade, qui va donc utiliser 3 paramètres : le nombre de ligne que va faire la tortue ; la couleur de départ ; la couleur de fin et effectuer une opération mathématique.’

for i in range(n):
    r=int(degrade(s[0], e[0], i, n))
    g=int(degrade(s[1], e[1], i, n))
    b=int(degrade(s[2], e[2], i, n))

    screen.colormode(255)
    pen.color(r, g, b)

Ici, on calcule la nuance de bleu qui va être utilisé à chaque ligne du dégradé.

    screen.colormode(255)
    pen.color(r, g, b)

screen.colormode(255) : Cette fonction définit le mode de couleur de l’écran sur une échelle de 0 à 255. Par défaut, Turtle utilise une échelle de couleur de 0 à 1 (où chaque couleur est une valeur décimale entre 0 et 1).

pen.color(r, g, b) : Cette fonction définit la couleur du stylo en utilisant trois valeurs pour le rouge (r), le vert (g), et le bleu (b). Ces valeurs doivent être comprises entre 0 et 255.

 pen.goto(-w/2, h/2 - i * (h / n))
    pen.begin_fill()
    for _ in range(2):
        pen.forward(w)
        pen.right(90)
        pen.forward(h / n)
        pen.right(90)
    pen.end_fill()

Ici, on délimite le rectangle dans lequel le dégradé doit être créer.

Les avions :

Ensuite nous avons créer une fonction avion qui créer nos avions :

def avion(x,y):
    pen.pu()
    pen.goto(x,y)
    pen.pd()
    pen.color("#9f9f9f")
    pen.begin_fill()
    pen.goto(x-7,y-30)
    pen.goto(x-10,y-60)
    pen.goto(x-80,y-160)
    pen.goto(x-10,y-160)
    pen.goto(x-10,y-175)
    pen.goto(x,y-175)
    pen.goto(x+10,y-175)
    pen.goto(x+10,y-160)
    pen.goto(x+80,y-160)
    pen.goto(x+10,y-60)
    pen.goto(x+7,y-30)
    pen.goto(x,y)
    pen.end_fill()

Tout d’abord, nous avons créé une fonction avion. Dans cette partie du code, nous dessinons la silhouette de l’avion à l’aide de goto(), nous lui donnons une couleur avec color(), puis nous la remplissons avec begin_fill() et end_fill().

	pen.pu()
    pen.goto(x-10,y-60)
    pen.pd()
    pen.color("#999999")
    pen.begin_fill()
    pen.goto(x-10,y-160)
    pen.goto(x-80,y-160)
    pen.goto(x-10,y-60)
    pen.end_fill()
    pen.pu()
    pen.goto(x+10,y-60)
    pen.pd()
    pen.begin_fill()
    pen.goto(x+10,y-160)
    pen.goto(x+80,y-160)
    pen.goto(x+10,y-60)
    pen.end_fill()

Ici, nous façonnons l’ombre sur les ailes en leur donnant une couleur plus sombre,

	pen.pu()
    pen.goto(x,y-20)
    pen.pd()
    pen.color("#606060")
    pen.begin_fill()
    pen.goto(x-5,y-40)
    pen.goto(x-5,y-50)
    pen.goto(x,y-70)
    pen.goto(x+5,y-50)
    pen.goto(x+5,y-40)
    pen.goto(x,y-20)
    pen.end_fill()

Cette partie nous permet de faire le cockpit,

    pen.pu()
    pen.goto(x-40,y-150)
    pen.pd()
    pen.color("red")
    pen.begin_fill()
    pen.circle(10)
    pen.end_fill()
    pen.pu()
    pen.goto(x-40,y-147)
    pen.pd()
    pen.color("#ffffff")
    pen.begin_fill()
    pen.circle(7)
    pen.end_fill()
    pen.pu()
    pen.goto(x-40,y-144)
    pen.pd()
    pen.color("blue")
    pen.begin_fill()
    pen.circle(4)
    pen.end_fill()
    pen.pu()
    pen.goto(x+40,y-150)
    pen.pd()
    pen.color("red")
    pen.begin_fill()
    pen.circle(10)
    pen.end_fill()
    pen.pu()
    pen.goto(x+40,y-147)
    pen.pd()
    pen.color("#ffffff")
    pen.begin_fill()
    pen.circle(7)
    pen.end_fill()
    pen.pu()
    pen.goto(x+40,y-144)
    pen.pd()
    pen.color("blue")
    pen.begin_fill()
    pen.circle(4)
    pen.end_fill()

Et pour finir, ce long script nous permet de faire les cocardes tricolores françaises.

Les traînés :

Afin d’avoir trois traîner au couleur de la France, nous avons créer trois fonction : traine_bleu ; traine_rouge ; traine_blanche.

def traine_bleu(x,y):

    pu()
    goto(x,y)
    pd()
    begin_fill()
    color("#00034e")
    goto(x-15,y-560)
    goto(x+15,y-560)
    goto(x,y)
    end_fill()
    pu()
def traine_blanc(x,y):

    pu()
    goto(x,y)
    pd()
    begin_fill()
    color("#ffffff")
    goto(x-15,y-560)
    goto(x+15,y-560)
    goto(x,y)
    end_fill()
    pu()
def traine_rouge(x,y):

    pu()
    goto(x,y)
    pd()
    begin_fill()
    color("#c30000")
    goto(x-15,y-560)
    goto(x+15,y-560)
    goto(x,y)
    end_fill()
    pu()

Pour créer les traîners, nous avons simplement créé une fonction de base où nous avons changé la couleur.

Les problèmes rencontrés :

Lorsque nous avons voulu générer nos avions avec les traînées à l’arrière nous avons obtenue l’image suivante :

Le problème venait du fait que nous avions dessiner les avions avant de dessiner les traînées. Donc les traînées sont apparues par dessus les avions.

Nos sources :

Afin de réaliser le code nous avons utiliser le site python.org, ainsi que nos connaissances. Nous avons aussi pris le temps de regarder les travaux des années précédant de la catégories art.

Image finale :

Voici le moment tant attendu l’image finale :

Télécharger notre script :

Projets

Perspective : Paris 2024

Dans le cadre de notre projet d’art génératif en Première NSI, nous avons décidé de concevoir un paysage en perspective qui représente Paris pendant les Jeux Olympiques de 2024. Notre objectif est de mêler des éléments architecturaux emblématiques de la ville avec l’ambiance olympique qui y règne.

Note : 4.5 sur 5.

Origine des JO

Cet été 2024 à Paris a eu lieu les JO. Les JO sont des événements sportifs internationaux majeurs, regroupant les sports d’été ou d’hiver, auxquels des milliers d’athlètes participent à travers différentes compétitions tous les quatre ans, pour chaque olympiade moderne.

Pour plus d’informations, nous vous conseillons cette vidéo explicative de l’origine des JO.

Le Projet

Revenons au projet. Nous avons choisi de créer une image des Champs Elysées pendant la période des JO 2024. Pour cela, nous utilisons les modules turtle et random. Le module turtle permet de dessiner l’image, tandis que le module random génère aléatoirement la taille et la position des étoiles dans le ciel. Nous avons également intégré un script pour exporter l’image générée par turtle en format .png, que vous pouvez retrouver ici.

Structure du script

Nous avons structuré le script en séparant chaque élément de l’image (fond dégradé, étoiles, route, trottoirs, arc de triomphe, anneaux olympiques, bâtiments, arbres) dans des fonctions distinctes. Ensuite, nous avons appelé ces fonctions dans un ordre spécifique, permettant ainsi à chaque élément de se superposer correctement dans son propre plan.

Analyse du script

Nous allons donc analyser notre script.

On commence d’abord par l’appel des modules utilisés et la mise en place turtle.

from turtle import *
from random import randint

# Vérification des modules importés
try:
    from PIL import Image
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False

# Définir le titre de la fenêtre de turtle 
titre = "Perspective Paris 2024 - construite avec turtle"
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")

colormode(255) # Permet l'utilisation de couleurs rgb
setup(1280, 720) # Définir la taille de la fenêtre en 720p 
speed(0) # La tortue va à la vitesse du lièvre
hideturtle() # La tortue active sa cape d'invisibilité

flash = True # False par défaut, on peut mettre True sinon, ou mieux 0x2A
if flash:
    wn = Screen()
    wn.tracer(0)

On utilise les modules turtle et random, en se concentrant uniquement sur la fonction randint de random. Le script inclut également une partie pour exporter une image générée par turtle. Cela permet de vérifier si l’utilisateur a installé le module PIL ; si ce n’est pas le cas, un message d’erreur s’affiche avec un lien pour l’installer, sans interrompre l’exécution du script. Ensuite, on définit le titre de la fenêtre qui affichera le rendu ainsi que sa taille. On configure également le type de couleurs utilisées (R,G,B), la vitesse de la tortue et on cache la tortue pour une présentation plus esthétique.

Puis, voici notre première fonction : le fond dégradé sur la partie haute de l’image.

def fond():
    red, green, blue = 0, 0, 100
    penup()
    hauteur = 0
    penup(), goto(-641, hauteur)
    while hauteur != 360:
        pendown()
        pencolor(round(red), round(green), round(blue))
        forward(1280)
        hauteur += 1
        goto(-640, hauteur)
        blue = blue - float(100 / 360)

Pour le fond, nous avons donc choisi d’utiliser un dégradé. Le script fait avancer la tortue sur une ligne d’un pixel de large et à la fin de chaque ligne, la tortue est envoyée à la ligne suivante grâce à un goto. À chaque déplacement, on ajuste progressivement les valeurs des couleurs, de façon arrondie (dans notre cas, on ajuste seulement la valeur du bleu) en fonction de la différence entre la couleur de début et celle de fin du dégradé.

Ensuite, vient la deuxième fonction, celle des étoiles dans le ciel.

def etoiles (x_max1, x_max2, y_max1, y_max2, r_max1, r_max2, nb_etoiles):
    for i in range(nb_etoiles):
        x, y, r = randint(x_max1,x_max2), randint(y_max1,y_max2), randint(r_max1,r_max2)
        pencolor("#FFFFFF"), begin_fill()
        penup(), goto(x, y), pendown()
        circle (r)
        fillcolor("#FFFFFF"), end_fill()

Pour les étoiles, leur position dans le ciel et leur taille, variant entre 1 et 3 pixels de rayon, sont déterminées aléatoirement avec la fonction randint du module random. Ensuite, un cercle est tracé à chaque position générée avec la taille correspondante. Ce processus est répété 100 fois dans une boucle for i in range, afin de créer 100 étoiles au total.

Vient ensuite la fonction pour dessiner la route et les trottoirs (le script suivant n’est qu’une partie du code complet, long et répétitif, il n’est donc pas nécessaire de commenter la suite).

def base_route(xi, yi, xf, yf, col_route, larg_debut_route, larg_fin_route):
    penup(), goto(xi, yi), pendown()
    pencolor(col_route)
    begin_fill(), fillcolor(col_route)
    goto(xi - (1 / 2 * larg_debut_route), yi), goto(xi + (1 / 2 * larg_debut_route), yi), goto(xf + (1 / 2 * larg_fin_route), yf)
    goto(xf - (1 / 2 * larg_fin_route), yf), goto(xi - (1 / 2 * larg_debut_route), yi)
    end_fill()
    
def ligne_entre_routes(xi, yi, xf, yf, col_lignes):
    penup(), goto(xi, yi), pendown()
    left(90)
    size = 20
    for i in range (357):
        pensize(round(size))
        size = size - float(15 / 360)
        pencolor(col_lignes)
        forward(1)
        
def marquage_gauche(xi, yi, xf, yf, col_lignes, long_marquages, larg_debut_route):
    longueur = 360
    penup(), goto(xi - 1 / 4 * larg_debut_route, yi), pendown()
    pensize(7), pencolor(col_lignes)
    right(40)
    k = 0
    while k * long_marquages < longueur - long_marquages:
        k += 2
        pendown(), forward(long_marquages), penup(), forward(long_marquages)

def trottoir_droite(xi, yi, xf, yf, col_trottoir, larg_debut_route, larg_fin_route, larg_debut_trot, larg_fin_trot):
    penup(), goto(xi + (1 / 2 * larg_debut_route), yi), pendown()
    pencolor(col_trottoir)
    pensize(2)
    begin_fill(), fillcolor(col_trottoir)
    goto(xi + (1 / 2 * larg_debut_route) + (1 / 2 * larg_debut_trot), yi), goto(xf + (1 / 2 * larg_fin_route) + (1 / 2 * larg_fin_trot), yf)
    goto(xf + (1 / 2 * larg_fin_route), yf), goto(xi + (1 / 2 * larg_debut_route), yi)
    end_fill()
    penup(), goto(xi + (1 / 2 * larg_debut_route) + 20, yi), pendown()
    pensize(3), pencolor("#6C757D")
    begin_fill(), fillcolor("#6C757D")
    goto(xf + (1 / 2 * larg_fin_route) + 7.5, yf), goto(xf + (1 / 2 * larg_fin_route), yf)
    goto(xi + (1 / 2 * larg_debut_route), yi), goto(xi + (1 / 2 * larg_debut_route) + 20, yi)
    end_fill()

def route_et_trottoir(xi, yi, xf, yf, col_route, col_trottoir, col_lignes, col_pavage, long_marquages, larg_debut_route,
larg_fin_route, larg_debut_trot, larg_fin_trot):
    base_route(xi, yi, xf, yf, col_route, larg_debut_route, larg_fin_route)
    ligne_entre_routes(xi, yi, xf, yf, col_lignes)
    marquage_droit(xi, yi, xf, yf, col_lignes, long_marquages, larg_debut_route)
    marquage_gauche(xi, yi, xf, yf, col_lignes, long_marquages, larg_debut_route)
    trottoir_droite(xi, yi, xf, yf, col_trottoir, larg_debut_route, larg_fin_route, larg_debut_trot, larg_fin_trot)
    trottoir_gauche(xi, yi, xf, yf, col_trottoir, larg_debut_route, larg_fin_route, larg_debut_trot, larg_fin_trot)

Pour la route et les trottoirs, nous commençons par tracer la base de la route en perspective en utilisant la fonction goto, puis nous ajoutons les marquages au sol. La ligne centrale entre les deux voies est progressivement affinée pour renforcer l’effet de perspective. Ensuite, les trottoirs de chaque côté sont tracés avec goto, en appliquant les mêmes proportions pour respecter la perspective. Enfin, une fonction finale regroupe toutes les fonctions constituant notre route et nos trottoirs.

Nous poursuivrons ensuite avec le codage de l’Arc de Triomphe (le script est encore long, il ne sera donc pas intégral ici).

def pilier_gauche(x, y, larg, long, col):
    pencolor(col)
    begin_fill(), fillcolor(col)
    penup(), goto(x - larg,y), pendown()
    right(70)
    for i in range(2):
        forward(larg), left(90), forward(long - 40), left(90)
    end_fill()
    
def atique_arc_de_triomphe(x, y, larg, long, col):
    pencolor(col)
    penup(), goto(x + larg, y + long - 40), left(90), pendown()
    begin_fill()
    circle((1 / 2 * larg), 180)
    for i in range(2):
        right(90), forward(larg)
    right(90), forward(3 / 2 * (long))
    for i in range(2):
        right(90), forward(larg)
    fillcolor(col), end_fill()
    
def rajout_atique(x, y, larg, long, col):
    pencolor(col)
    penup(), goto(x + (long), y + ((long - 40) + larg)), pendown()
    begin_fill(), fillcolor(col)
    right(90)
    for i in range(2):
        forward(larg - (1 / 2 * larg)), left(90), forward((long) + larg), left(90)
    end_fill()

def details_arc_de_triomphe(x, y, larg, long, col_detail):
    penup(), goto(x + (long), y + (long - 40 + larg)), pendown()
    pensize(5), pencolor(col_detail)
    left(90), forward(long + larg)

def arc_de_triomphe(x, y, larg, long, col):
    pilier_gauche(x, y, larg, long, col)
    pilier_droite(x, y, larg, long, col)
    atique_arc_de_triomphe(x, y, larg, long, col)
    rajout_atique(x, y, larg, long, col)
    details_arc_de_triomphe(x, y, larg, long, "#9C7F6E")

Pour l’Arc de Triomphe, nous avons choisi de le coder étape par étape : d’abord les piliers, puis l’attique, et enfin les détails. Les piliers sont représentés par de simples rectangles colorés, en utilisant les commandes begin_fill, fillcolor, et end_fill. Pour l’attique, nous avons dessiné un arc de cercle avec circle(rayon, 180) pour représenter la courbure. Enfin, toutes les fonctions constituant l’Arc de Triomphe sont appelées dans une fonction unique pour générer l’ensemble du monument.

Passons maintenant au code pour générer les anneaux olympiques.

list_col_anneau = ["#0D79FF", "#FFD900", "#000000", "#12C52D", "#FF0F0F"]

def anneau_haut(x,col,rayon,size):
    penup(), forward(x), pendown()
    pencolor(col), pensize(size), circle(rayon)
 
def anneau_bas(x,col,rayon,size):
    penup(), left(90), forward(x), pendown()
    pencolor(col), pensize(size), circle(rayon)

def anneaux(xf,yf,rayon,size):
    penup(), goto(xf,yf), pendown()
    anneau_haut(0,list_col_anneau[0],rayon,size)
    anneau_haut(2 * rayon + (size + 5),list_col_anneau[2],rayon,size)
    anneau_haut(2 * rayon + (size + 5),list_col_anneau[4],rayon,size)
    penup(), goto(xf - rayon - (1 / 5 * rayon), yf - rayon), right(90), pendown()
    anneau_bas(0,list_col_anneau[1],rayon,size)
    right(90)
    anneau_bas(2 * rayon + (size + 5),list_col_anneau[3],rayon,size)

Nous avons commencé par définir une liste des couleurs pour chaque anneau. Ensuite, les anneaux de la rangée supérieure ont été dessinés, suivis de ceux de la rangée inférieure, en utilisant la fonction circle. Enfin, nous avons regroupé le tout dans une fonction unique qui positionne les anneaux correctement.

Nous poursuivrons ensuite avec le script dédié à la lune et à ses cratères (le code des cratères étant répétitif, il ne sera pas présenté dans sa totalité).

def cratere1 (x, y, rayon_lune, col):
    x_crat1 = x
    y_crat1 = y + rayon_lune + (1 / 4 * rayon_lune)
    taille_crat1 = 1 / 3 * rayon_lune
    penup(), goto(x,y), goto(x_crat1,y_crat1), pendown()
    begin_fill(), pencolor(col)
    circle(taille_crat1)
    fillcolor(col), end_fill()

def crateres(x, y, rayon_lune, col):
    cratere1 (x, y, rayon_lune, col)
    cratere2 (x, y, rayon_lune, col)
    cratere3 (x, y, rayon_lune, col)
    cratere4 (x, y, rayon_lune, col)
    cratere5 (x, y, rayon_lune, col)

def lune(x, y, rayon_lune, col_lune):
    left(180)
    begin_fill()
    penup(), goto(x,y), pendown()
    pencolor(col_lune), circle(rayon_lune), fillcolor(col_lune), end_fill()
    crateres(x, y, rayon_lune, "#DEDCDA")

A propos de la lune et de ses cratères, premièrement, chaque fonction (cratere1 à cratere5) dessine un cratère à des positions spécifiques autour de la lune, en utilisant le rayon de la lune pour ajuster la taille et la position. Deuxièmement, la fonction crateres regroupe tous les cratères en une seule fonction. Et au final, la fonction lune dessine la lune en tant que cercle rempli, puis appelle la fonction crateres pour ajouter les cratères à la surface de la lune.

Nous allons maintenant aborder le codage des bâtiments.

def bat_droite(xi, yi, xf, yf, col, col_trace, hauteur):
    pencolor(col_trace), pensize(3)
    begin_fill(), fillcolor(col)
    penup(), goto(xi, yi), pendown()
    goto(xf, yf), goto(xf, hauteur), goto(1282, hauteur), goto(xi, yi)
    end_fill()
    penup(), goto(xf, hauteur), pendown()
    right(15), forward(500)

def bat_gauche(xi, yi, xf, yf, col, col_trace, hauteur):
    pencolor(col_trace), pensize(3)
    begin_fill(), fillcolor(col)
    penup(), goto(xi, yi), pendown()
    goto(xf, yf), goto(xf, hauteur), goto(-1282, hauteur), goto(xi, yi)
    end_fill()
    penup(), goto(xf, hauteur), pendown()
    left(210), forward(500)

Pour les bâtiments, les fonctions dessinent des structures à droite et à gauche, en utilisant des coordonnées spécifiées, une couleur de remplissage, une couleur de contour et une hauteur. Chaque fonction remplit le bâtiment et trace un toit incliné, créant ainsi un effet de perspective qui enrichit visuellement l’image.

Nous allons maintenant découvrir le script qui permet de dessiner des arbres le long des trottoirs.

def tronc(x, y, larg_tronc, col_tronc, long_tronc):
    begin_fill()
    penup(), goto(x,y), pendown()
    pencolor(col_tronc)
    for i in range(2):
        forward(larg_tronc), left(90), forward(long_tronc), left(90)
    fillcolor(col_tronc), end_fill()
  
def feuilles(x, y, col_feuilles, ray_feuilles, larg_tronc, long_tronc, col_tronc):
    begin_fill()
    penup(), goto(x + (1 / 2 * larg_tronc),y + long_tronc), pendown()
    pencolor(col_feuilles)
    for i in range(5):
        begin_fill()
        circle(ray_feuilles), left(72), forward(10)
        fillcolor(col_feuilles), end_fill()

def arbre(x, y, col_feuilles, ray_feuilles, larg_tronc, long_tronc, col_tronc):
    tronc(x, y, larg_tronc, col_tronc, long_tronc)
    feuilles(x, y, col_feuilles, ray_feuilles, larg_tronc, long_tronc, col_tronc)

# On place les arbres sur le trottoir
def arbres():
    x = -275
    y = -25
    ray_feuilles = 20
    larg_tronc = 22
    long_tronc = 80
    right(195)
    for i in range(14):
        arbre(x, y, "#009B1A", ray_feuilles, larg_tronc, long_tronc, "#7F2C00")
        x -= 25
        y -= 25
        ray_feuilles += float(2.5)
        larg_tronc += float(2.5)
        long_tronc += 10
    x = 258
    y = -25
    ray_feuilles = 20
    larg_tronc = 22
    long_tronc = 80
    for i in range(14):
        arbre(x, y, "#009B1A", ray_feuilles, larg_tronc, long_tronc, "#7F2C00")
        x += 22
        y -= 25
        ray_feuilles += float(2.5)
        larg_tronc += float(2.5)
        long_tronc += 10

Concernant les arbres, tout d’abord, la fonction tronc dessine le tronc d’un arbre en utilisant des coordonnées spécifiques, une largeur, une couleur et une longueur. Elle utilise une boucle pour créer un rectangle rempli représentant le tronc. Après, la fonction feuille dessine les feuilles de l’arbre. Elle positionne le curseur au-dessus du tronc et utilise une boucle pour créer plusieurs cercles qui représentent les feuilles, disposés en étoile autour de la partie supérieure du tronc. Suite à quoi la fonction arbre combine les fonctions tronc et feuilles pour dessiner un arbre complet à des coordonnées données. Pour couronner le tout, la fonction arbres place plusieurs arbres le long des trottoirs. Elle commence à des coordonnées spécifiques et utilise une boucle pour dessiner 14 arbres de chaque côté. La position, la taille des feuilles, la largeur et la longueur du tronc augmentent progressivement pour créer un effet de perspective, tandis que les arbres se déplacent vers le bas et de manière symétrique des deux côtés.

Enfin, nous appelons chaque fonction dans un ordre spécifique pour produire l’image finale.

# Appel de toutes les fonctions
fond()
etoiles(-640, 640, 0, 360, 1, 3, 100)
route_et_trottoir(0, -360, 0, 0, "#424345", "#ADB5BD", "#CECDC9", "#6C757D", 30, 800, 300, 1000, 375)
arc_de_triomphe(-50, 0, 100, 200, "#D4C4B0")
anneaux(70,300,30,7)
lune(250, 200, 50, "#F4F1ED")
bat_droite(900, -360, 337.5, 0, "#eae2b7", "#d4a373", 250)
bat_gauche(-900, -360, -337.5, 0, "#eae2b7", "#d4a373", 250)
arbres()

Et pour exporter l’image finale en .png il y a la suite du script pour exporter une image générée par turtle en .png utilisé au début du script.

if flash:
    wn.update() 

# Enregistrement de l'image finale
image = getcanvas()
nom_du_fichier_sans_extension=titre+"_"+hex(randint(2**30+2**25,2**30+2**25+2**24-1))[2:]
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')
try:
    psimage = Image.open(nom_du_fichier_sans_extension+".ps")
    psimage.load(scale=2)
    psimage_resized = psimage.resize((1280, 720))
    psimage.save(nom_du_fichier_sans_extension+".png")
    print(nom_du_fichier_sans_extension+".png", psimage.size, "sauvegardé dans le dossier")    
 
# Vérification des modules importés
except:
    if not pillow_installed:
        print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
        print("https://nsi.xyz/py2png")
    else:
        print("Oops! - 'ghostscript' not installed- RTFM :")
        print("https://nsi.xyz/py2png")

exitonclick()

Le script va donc générer une image en .ps et la convertir en .png avec un nom généré aléatoirement.

Image finale

Télécharger le .py

Projets

Le code des supporters : virage Auteuil.

En se basant sur une photo du magnifique virage Auteuil, emblématique du Parc des Princes, stade du Paris-Saint-Germain, nous avons reproduit sous un angle de vue différent cette image. Cette reproduction met en avant la disposition du stade, du virage, mais surtout les couleurs emblématiques de ce club. Notre image possède une perspective appuyant bien sur les aspects clés de l’image cités auparavant.

Pour le fond, nous avons appelé deux fonctions rectangle chacune ayant la même hauteur et la même largeur pour respecter les proportionnalités et les avons choisies rouge et bleu en référence aux couleurs du club.

rectangle modifiable

# rectangle modifiable
def rectangle(x,y,z,g,a,b):
  def longueur(a):
    a == 0 + a
    forward(a)
  def largeur(b):
    b == 0 + b
    forward(b)
  pensize(z)
  speed(100)
  # Remplissage selon la couleur donnée
  penup()
  begin_fill()
  color(g)
  goto (x,y), pendown(),
  for i in range(2):
      longueur(a), right(90), largeur(b), right (90)
  end_fill()
  penup()  

rectangle(-858,538,1,"red",858,1076)
rectangle(858,538,1,"blue",-858,1076)

Pour commencer à dessiner une perspective, nous avons réalisé une pelouse composée de formes géométriques différentes en prenant pour exemple notre image référence. Les teintes sont différentes en fonction des zones sur la pelouse pour réaliser les sens de tonte et se rapprocher ainsi au maximum d’un vrai stade.

Pelouse

## Formation en perspective de la pelouse
# Triangles
def triangle(a,b,x,y,c,d,e,f):
    begin_fill()
    pensize(a)
    color(b)
    penup(), goto(x,y), pendown(), goto(c,d), goto(e,f), goto(x,y), penup()
    end_fill()

def polygone_1(a,b,x,y,c,d,e,f,g,h):
    begin_fill()
    pensize(a)
    color(b)
    penup(), goto(x,y), pendown(), goto(c,d), goto(e,f), goto(g,h), goto(x,y), penup()
    end_fill()

def polygone_2(a,b,x,y,c,d,e,f,g,h,i,j):
    begin_fill()
    pensize(a)
    color(b)
    penup(), goto(x,y), pendown(), goto(c,d), goto(e,f), goto(g,h), goto(i,j), goto(x,y), penup()
    end_fill()

# Formes pelouse verte différentes
rectangle(-858,-414,1,"green",1716,124)
polygone_1(1,"green",-820,-414,-750,-414,-858,-474,-858,-424)
polygone_2(1,"darkgreen",-750,-414,-660,-414,-820,-538,-858,-538,-858,-474)
polygone_1(1,"darkgreen",-660,-414,-550,-414,-680,-538,-820,-538)
polygone_1(1,"darkgreen",-300,-414,0,-414,0,-538,-330,-538)
polygone_1(1,"darkgreen",300,-414,550,-414,670,-538,330,-538)
triangle(1,"darkgreen",750,-414,858,-414,858,-474)

Toujours en appelant la fonction rectangle utilisée au tout début, nous avons réalisé un panneau publicitaire de couleur grise, comme on le voit dans les stades.

Panneau publicitaire

rectangle(-858,-377,1,"grey",1716,37)

Pour commencer la perspective des gradins, nous avons tracé des courbes horizontales qui descendent en se rapprochant du centre.

Perspective des tribunes avec traits horizontaux gris

penup(), goto(-858,40), pendown()
 
penup(), goto(-858,130), pendown()
def trait_coupe():
    for x in range(-858,864):
       y = -90+(x / 300) ** 4
       goto(x,y)
       pendown()        
trait_coupe()

penup(), goto(-858,190), pendown()
def trait_coupe1():
    for x in range(-858,864):
       y = -30+(x / 300) ** 4
       goto(x,y)
       pendown()        
trait_coupe1()

penup(), goto(-858,250), pendown()
def trait_coupe2():
    for x in range(-858,864):
       y = 30+(x / 300) ** 4
       goto(x,y)
       pendown()        
trait_coupe2()

penup(), goto(-858,310), pendown()    
def trait_coupe3():
    for x in range(-858,864):
        y = 90+(x / 300) ** 4
        goto(x,y)
        pendown()        
trait_coupe3()

penup(), goto(-858,370), pendown()    
def trait_coupe4():
    for x in range(-858,864):
        y = 150+(x / 300) ** 4
        goto(x,y)
        pendown()        
trait_coupe4()

penup(), goto(-858,430), pendown()    
def trait_coupe5():
    for x in range(-858,864):
        y = 210+(x / 300) ** 4
        goto(x,y)
        pendown()        
trait_coupe5()

penup(), goto(-858,490), pendown()    
def trait_coupe6():
    for x in range(-858,864):
        y = 270+(x / 300) ** 4
        goto(x,y)
        pendown()        
trait_coupe6()

penup(), goto(-858,550), pendown()    
def trait_coupe7():
    for x in range(-858,864):
        y = 330+(x / 300) ** 4
        goto(x,y)
        pendown()        
trait_coupe7()

penup(), goto(-858,70), pendown()
def trait_coupe11():
    for x in range(-858,864):
       y = -150+(x / 300) ** 4
       goto(x,y)
       pendown()        
trait_coupe11()

penup(), goto(-858,10), pendown()
def trait_coupe12():
    for x in range(-858,864):
       y = -210+(x / 300) ** 4
       goto(x,y)
       pendown()        
trait_coupe12()

# Zone grise au dessus des gradins
def zone_grise():
    pensize(1)
    color("grey")
    penup(), goto(-858,438), begin_fill(), pendown(), trait_coupe7(), goto(858,476), goto(-858,476), goto(-858,438), end_fill(), penup()
zone_grise()

# Zone grise en bas des gradins
def zone_grise_1():
    pensize(1)
    color("grey")
    penup(), goto(-858,10), begin_fill(), pendown(), trait_coupe12(), goto(858,-377), goto(-858,-377), goto(-858,10), end_fill(), penup()
zone_grise_1()

Pour approfondir cette perspective, nous avons décidé de compléter la zone sous la banderole et au dessus de l’herbe avec du gris foncé.

Remplissage avec du gris foncé

# Zone grise au dessus des gradins
def zone_grise():
    pensize(1)
    color("grey")
    penup(), goto(-858,438), begin_fill(), pendown(), trait_coupe7(), goto(858,476), goto(-858,476), goto(-858,438), end_fill(), penup()
zone_grise()

# Zone grise en bas des gradins
def zone_grise_1():
    pensize(1)
    color("grey")
    penup(), goto(-858,10), begin_fill(), pendown(), trait_coupe12(), goto(858,-377), goto(-858,-377), goto(-858,10), end_fill(), penup()
zone_grise_1()

Pour donner une impression de hauteur en plus de la perspective dans la largeur, nous avons dessiné des traits verticaux qui font se rétrécir les cases au fur et à mesure que l’on se déplace sur les côtés de l’image.

Ajout des traits verticaux

# Traits de coupe verticaux
def trait_de_coupe_vertical(x,y,a,b):
    pensize(3)
    color("darkgrey")
    penup(), goto(x,y), pendown(), goto(a,b)
    
trait_de_coupe_vertical(0,329,0,-208)
trait_de_coupe_vertical(-80,329,-100,-208)
trait_de_coupe_vertical(80,329,100,-208)
trait_de_coupe_vertical(155,329,200,-208)
trait_de_coupe_vertical(-155,329,-200,-208)
trait_de_coupe_vertical(-225,330,-300,-207)
trait_de_coupe_vertical(225,330,300,-207)
trait_de_coupe_vertical(295,330,400,-206)
trait_de_coupe_vertical(-295,330,-400,-206)
trait_de_coupe_vertical(385,333,500,-202)
trait_de_coupe_vertical(-385,333,-500,-202)
trait_de_coupe_vertical(455,335,600,-194)
trait_de_coupe_vertical(-455,335,-600,-194)
trait_de_coupe_vertical(525,339,700,-179)
trait_de_coupe_vertical(-525,339,-700,-179)
trait_de_coupe_vertical(595,345,800,-159)
trait_de_coupe_vertical(-595,345,-800,-159)
trait_de_coupe_vertical(-665,355,-900,-185)
trait_de_coupe_vertical(665,355,900,-185)
trait_de_coupe_vertical(735,368,1000,-185)
trait_de_coupe_vertical(-735,368,-1000,-185)
trait_de_coupe_vertical(795,381,1100,-185)
trait_de_coupe_vertical(-795,381,-1100,-185)

Pour la cage, nous avons réalisé une fonction cage contenant une fonction pour les filets, une pour les montants et une pour les poteaux à côté. Tous sont reliés par des traits de contour blancs qui donnent cette impression de perspective. Un trait de contour noir se trouve en bas pour séparer la pelouse du reste.

Formation de la cage et des contours

def cage():
# Formation du cadre
	def cadre():
		pensize(5)
		penup(),color("white")
		goto(-429,-411)
		pendown()
		goto(-429,-200), right(90), goto(429,-200), right(90), goto(429,-411)
		penup()
	cadre()
# Formation du filet
	def filet():
		def traits_verticaux():
			color("white")
			pensize(1)
			penup(), goto(-395,-411), pendown(), goto(-395,-221), penup(), goto(-360,-411), pendown(), goto(-360,-221), penup(), goto(-326,-411), pendown(), goto(-326,-221), penup(), goto(-291,-411), pendown(), goto(-291,-221), penup(), goto(-257,-411), pendown(), goto(-257,-221), penup(), goto(-223,-411), pendown(), goto(-223,-221), penup(), goto(-189,-411), pendown(), goto(-189,-221), penup(), goto(-154,-411), pendown(), goto(-154,-221), penup(), goto(-120,-411), pendown(), goto(-120,-221), penup(), goto(-86,-411), pendown(), goto(-86,-221), penup(), goto(-52,-411), pendown(), goto(-52,-221), penup(), goto(-17,-413), pendown(), goto(-17,-221), penup(), goto(17,-411), pendown(), goto(17,-221), penup(), goto(51,-411), pendown(), goto(51,-221), penup(), goto(85,-411), pendown(), goto(85,-221), penup(), goto(120,-411), pendown(), goto(120,-221), penup(), goto(154,-411), pendown(), goto(154,-221), penup(), goto(188,-411), pendown(), goto(188,-221), penup(), goto(224,-411), pendown(), goto(224,-221), penup(), goto(259,-411), pendown(), goto(259,-221), penup(), goto(293,-411), pendown(), goto(293,-221), penup(), goto(327,-411), pendown(), goto(327,-221), penup(), goto(361,-411), pendown(), goto(361,-221), penup(), goto(395,-411), pendown(), goto(395,-221)
		traits_verticaux()
		def traits_horizontaux():
			color("white")
			pensize(1)
			penup(), goto(-405,-390), pendown(), goto(405,-390), penup(), goto(-405,-369), pendown(), goto(405,-369), penup(), goto(-405,-348), pendown(), goto(405,-348), penup(), goto(-405,-327), pendown(), goto(405,-327), penup(), goto(-405,-305), pendown(), goto(405,-305), penup(), goto(-405,-284), pendown(), goto(405,-284), penup(), goto(-405,-263), pendown(), goto(405,-263), penup(), goto(-405,-242), pendown(), goto(405,-242), penup(), goto(-405,-221), pendown(), goto(405,-221), penup()    
        traits_horizontaux()
	filet()
# Formation des poteaux
	def poteaux():
    	pensize(3)
    	color("black")
    	def poteau_1():
        	penup(), goto(-490,-411), pendown(), goto(-490,-170), penup()
    	poteau_1()
    	def poteau_2():
        	penup(), goto(490,-411), pendown(), goto(490,-170), penup()
    	poteau_2()
	poteaux()
	def traits_poteaux_cages():
    	def trait_poteau_cages_1():
        	pensize(2)
        	color("white")
        	penup(), goto(-490,-170), pendown(), goto(-429,-200), penup()
    	trait_poteau_cages_1()
    	def trait_poteau_cages_2():
        	pensize(2)
        	color("white")
        	penup(), goto(490,-170), pendown(), goto(429,-200), penup()
    	trait_poteau_cages_2()
	traits_poteaux_cages()
cage()

def trait_contour(a,z,x,y,b,c):
	pensize(a)
	color(z)
	penup(), goto(x,y), pendown(), goto(b,c)

trait_contour(2,"black",-858,-414,858,-414)
trait_contour(2,"white",-429,-200,-405,-221)
trait_contour(2,"white",429,-200,405,-221)
trait_contour(2,"white",-405,-221,-405,-411)
trait_contour(2,"white",405,-221,405,-411)

Pour former la banderole bleu foncée, nous avons fait appel à la fonction rectangle et pour tracer les lettres, nous avons utilisé des rectangles, des cercles et une fonction trapèze spécialement conçue pour la queue du « R »

Formation de la banderole avec le slogan

# Banderole en haut de l'image
def banderole():
    rectangle(858,438,1,"darkblue",1716,100)
    penup(), goto(-560,450), pendown(), rectangle(-560,450,1,"white",20,76), penup(), goto(-480,526), pendown(), begin_fill(), circle(39), end_fill(), penup(), goto(-463,511), color("darkblue"), pendown(), begin_fill(), circle(25), end_fill(), penup(), goto(-446,504), pendown(), rectangle(-400,450,1,"white",20,76), penup(), goto(-215,526), begin_fill(), color("white"), circle(39), end_fill(), penup(), goto(-198,511), color("darkblue"), pendown(), begin_fill(), circle(25), end_fill(), penup(), goto(-145,505), pendown(), begin_fill(), rectangle(-145,505,1,"white",10,20), end_fill(), penup(), goto(-105,450), pendown(), rectangle(-105,450,1,"white",20,76), penup(), goto(-125,511), pendown(), rectangle(-65,511,1,"white",40,15), penup(), goto(-65,480), pendown(), rectangle(-65,480,1,"white",40,15), penup(), goto(-65,450), pendown(), rectangle(-65,450,1,"white",40,15), penup(), goto(-15,530), pendown(), begin_fill(), circle(30), end_fill(), penup(), goto(-15,507), pendown(), begin_fill(), circle(30), end_fill(), penup(), goto(-40,491), begin_fill(), color("darkblue"), pendown(), circle(19), end_fill(), penup(), goto(11,525), begin_fill(), color("darkblue"), pendown(), circle(19), end_fill(), penup(), goto(90,450), pendown(), rectangle(70,450,1,"white",20,76), penup(), pendown(), rectangle(85,506,1,"white",50,20), penup(), rectangle(225,450,1,"white",20,76), penup(), goto(243,526), begin_fill(), color("white"), circle(25), end_fill(), penup(), goto(243,513), begin_fill(), color("darkblue"), circle(12), end_fill(), penup(), rectangle(305,450,1,"white",15,76), penup(), rectangle(340,511,1,"white",35,15), penup(), rectangle(345,450,1,"white",15,76), penup(), rectangle(345,480,1,"white",40,15), rectangle(388,450,1,"white",15,76), penup(), goto(406,526), begin_fill(), color("white"), circle(25), end_fill(), penup(), goto(406,513), begin_fill(), color("darkblue"), circle(12), end_fill(), penup(), trapeze("white",1,388,483,410,483,430,450,410,450), penup(), rectangle(470,450,1,"white",20,76), goto(520,530), pendown(), begin_fill(), circle(30), end_fill(), penup(), goto(520,507), pendown(), begin_fill(), circle(30), end_fill(), penup(), goto(494,491), begin_fill(), color("darkblue"), pendown(), circle(19), end_fill(), penup(), goto(545,525), begin_fill(), color("darkblue"), pendown(), circle(19), end_fill()  


banderole()

Rendu final

Projets

Avions de l’armée américaine

Nous avions donc comme premier projet de première de réaliser une image qui se génère grâce à code python que nous devions créer sur une durée de un mois. Nous avons donc choisi de générer un F-16, F-22 et F-35 pour ce projet. Nous devions utiliser le module turtle pour l’image.

Origines de ces avions

Le F-16 est un avion de combat à rôles multiples de l’armée américaine créé dans les années 70.
Le F-22 est un avion de chasse furtif de l’armée américaine créé dans les années 90.
Le F-35 est un avion de combat à rôles multiples de l’armée américaine créé dans les années 2000.

Organisation

Pour réussir à faire le script nous avons dû séparer les différentes parties du code pour s’y repérer. Cela nous a donc permis de faire des appels de fonctions dans le code (rectangle, triangle, étoile, trapèze).
Faire appel à des fonctions permet de réduire la taille du code, cela été utile étant donné la limite de 404 lignes.


Le Script

Nous allons commencer par le début du code

from turtle import *
from random import randint

try:
    from PIL import Image
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False

titre = "Avions de l'armée américaine - F16 F22 F35"
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")

setup(1280, 720)
speed(0)
colormode(255)
hideturtle()

flash = True
if flash:
    wn = Screen()
    wn.tracer(0)

Il permet d’importer les modules nécessaires et initialiser la plupart du code (comme la couleur ou pillow).
« flash » est une nouveauté, il permet de générer instantanément l’image.


LA SAUVEGARDE DES IMAGES

Pour sauvegarder l’image on nous a donné ce code ci-dessous :

image = getcanvas()
nom_du_fichier_sans_extension=titre+"_"+hex(randint(2**30+2**25,2**30+2**25+2**24-1))[2:]

# Génère un fichier .ps
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')

# Ouvre le fichier .ps et génère le fichier .png


try:
    psimage = Image.open(nom_du_fichier_sans_extension+".ps")
    psimage.load(scale=2)
    psimage_resized = psimage.resize((1280, 720))
    psimage.save(nom_du_fichier_sans_extension+".png")
    print(nom_du_fichier_sans_extension+".png", psimage.size, "sauvegardé dans le dossier")    
        
      except:
    if not pillow_installed:
        print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
        print("https://nsi.xyz/py2png")
    else:
        print("Oops! - 'ghostscript' not installed- RTFM :")
        print("https://nsi.xyz/py2png")

Or nous avions besoin d’enregistrer plusieurs images pour montrer les différentes étapes. Nous avons donc transformé ce code en une fonction nommée save() qui permet de sauvegarder l’image à n’importe quel moment dans le code.


LE FOND

La première fonction « drapeau » est le fond de l’image

def drapeau():        
    ligne(-640,640,13)
    rectangle(-640,-24,640,-385,0,(35,45,100))
    pos_y = 325
    for j in range(1,6):
        for i in range(1,7):
            étoile(-700+106*i,pos_y,30)
        pos_y = pos_y-70
    pos_y = 290
    for j in range(1,5):
        for i in range(1,6):
            étoile(-647+106*i,pos_y,30)
        pos_y = pos_y-70

Cette fonction fait elle-même appel à d’autres fonctions. Commençons par la première fonction : « ligne »

def ligne(x,y,repeat):
    for i in range(repeat):
        rectangle(x,y,1280,-720/13,0,(150,20,30))
        y = y-720/13
        rectangle(x,y,1280,-720/13,0,(255,255,255))
        y = y-720/13

La fonction ligne permet de faire toutes les lignes du drapeau soit 13 lignes ou 7 rouges et 6 blanches.
Et à chaque ligne il descend de la résolution de l’image divisée par 13 (car il y a 13 lignes)

Ensuite nous avons la création du rectangle bleu en haut à gauche du drapeau.

rectangle(-640,-24,640,-385,0,(35,45,100))

Ensuite il y a la fonction « étoile »

def étoile(x,y,L):
    setup(x,y,(255,255,255))
    pensize(5)
    for i in range(5):
        forward(L)
        right(144)
    pensize(1)
    end_fill()

La fonction « étoile » utilise « setup » qui est une fonction qui permet de faire le début de toutes les formes et qui est donc utilisée dans la plupart des fonctions.
Cette fonction permet de créer une étoile et avec notre boucle « for » utilisée dans la fonction drapeau on peut créer ainsi les 50 étoiles.


PREMIER AVION : F-16

Le premier avion se situera à gauche de l’image, nous avons donc fait le plus ancien en premier : le F-16

Avant même d’avoir commencé à coder, on a refait les avions avec des formes géométriques pour savoir quelles formes créer et où les mettre.
Voici comment nous avions découpé le F-16 avec plusieurs formes géométriques (des triangles et des rectangles).

# ======================== F-16 ========================

def F16_plane(x,y):
    triangle_iso(-50 + x, -90 + y, 100, 100, (100, 100, 100))
    triangle_iso(-100 + x, -20 + y, 200, 100, (80, 80, 80))
    triangle_iso(-25 + x, 60 + y, 50, 180, (100, 100, 100))
    rectangle(-100 + x, -20 + y, 200, 20, 0, (70, 70, 70))
    rectangle(-10 + x, -110 + y, 20, -250, 0, (100, 100, 100))
    rectangle(-20 + x, -70 + y, 40, -130, 0, (100, 100, 100))

    for i in range(0, 66, 65):
        rectangle(-50 + i + x, -110 + y, 35, -20, 0, (80, 80, 80))

    for i in range(0, 203, 202):
        rectangle(-102 + i + x, -42 + y, 2, -55, 0, (70, 70, 70))

# ======================== COCKPIT F-16 ========================

    rectangle(-10 + x, 130 + y, 20, 50, 0, (10, 10, 10))
    triangle_iso(-10 + x, 80 + y, 20, -15, (10, 10, 10))
    cercle(10, 0 + x, 130 + y, (10, 10, 10))

Nous avons donc utilisé ces formes là pour créer l’avion puis ensuite fait un cockpit en noir. On a joué avec l’obscurité des couleurs pour créer un effet de profondeur.


DEUXIEME AVION : F-22

Le deuxième avion se situera au milieu de l’image, ce sera donc le F-22

Tout comme le F-16 nous avons d’abord refait le F-22 avec des formes géométriques.
Voici comment nous avons refait le F-22 avec les formes géométriques (des triangles et des rectangles).

La forme finale n’est pas exactement la même car nous avons décidé de modifier certaines choses pour que la forme soit plus ressemblante.

# ======================== F-22 ========================

def F22_plane(x,y):

    triangle_iso(-150+x,-20+y,300,150, (155, 155, 155))
    rectangle(-150+x,-40+y,300,-20, 0, (155, 155, 155))
    triangle_iso(-150+x,-40+y,300,-50, (155, 155, 155))
    rectangle(-85+x,-85+y,60,45, 45, (155,155,155))
    rectangle(-85+x,-85+y,30,30, 0, (155,155,155))
    rectangle(85+x,-85+y,-60,45, -45, (155,155,155))
    rectangle(85+x,-85+y,-30,30, 0, (155,155,155))
    rectangle(-30+x,-85+y,60,-220, 0, (160, 160, 160))
    for i in range(0,31,30):
        triangle_iso(-25+i+x,-85+y,20,-10, (50, 50, 50))
    triangle_iso(-30+x,135+y,60,50, (160, 160, 160))
    rectangle(-15+x,140+y,30,-80, 0, (160, 160, 160))
    triangle_iso(-15+x,220+y,30,35, (160, 160, 160))
    
# ======================== COCKPIT F-22 ========================

    triangle_iso(-10+x,160+y,20,-15,(255,190,0))
    rectangle(-10+x,160+y,20,-30,0,(255,190,0))
    trapèze(-10+x,190+y,20,20,10,(255,190,0))
    triangle_iso(-5+x,210+y,10,5,(255,190,0))

Pour éviter de créer une fonction losange nous avons mis l’orientation de base de la fonction rectangle donc en mettant l’orientation à 45° ou -45° cela nous a permis d’avoir des losanges.


TROISIEME AVION : F-35

Le troisième avion se situera à droite de l’image, ce sera donc le plus récent : le F-35

Tout comme les deux autres avions nous avons d’abord refait le F-35 avec des formes géométriques.
Voici le schéma du F-35 fait avec des formes géométriques (triangles, rectangles et trapèzes). Cet avion est celui que nous avons dû le plus modifier

#========================= F-35 ================================
    
def F35_plane(x,y):
    
    triangle_iso(-125+x,-20+y,250,150,(130,130,130))
    rectangle(-125+x,-20+y,250,25,0,(130,130,130))
    triangle_iso(-125+x,-45+y,250,-45,(130,130,130))
    triangle_iso(-30+x,80+y,60,110,(130,130,130))
    trapèze(-50+x,60+y,30,60,20,(130,130,130))
    trapèze(20+x,60+y,30,60,20,(130,130,130))
    trapèze(-20+x,120+y,40,70,20,(130,130,130))
    triangle_iso(-10+x,190+y,20,15,(130,130,130))
    trapèze(-15+x,-85+y,30,-20,20,(50,50,50))
    rectangle(-50+x,-115+y,50,-40,45,(130,130,130))
    rectangle(-80+x,-89+y,40,30,0,(130,130,130))
    rectangle(80+x,-85+y,50,-40,135,(130,130,130))
    rectangle(40+x,-89+y,40,30,0,(130,130,130))
    
#========================== COCKPIT F-35 ========================
    
    trapèze(-9+x,160+y,18,-35,40,(15,15,15))
    trapèze(-9+x,160+y,18,15,5,(15,15,15))
    rectangle(-18+x,125+y,36,10,0,(15,15,15))
    trapèze(-18+x,115+y,36,-20,10,(15,15,15))

Le F-35 fut le plus compliqué à faire étant donné la fonction trapèze très complexe à utiliser mais après un moment nous avons réussit à le finir.

LE NOM DES AVIONS

Maintenant que nous avons les différents avions nous avons décidé de mettre leur nom sous chacun d’eux. Pour cela, il nous fallait générer les caractères : F – 1 2 3 5 6

Donc nous avons créé 7 fonctions pour les 7 caractères différents.

#========================= CARACTERES =====================

def F(x,y):
    rectangle(-50+x,y,10,100,0,(0,0,0))
    rectangle(-50+x,y-30,30,10,0,(0,0,0))
    rectangle(-50+x,y,50,10,0,(0,0,0))

def tiret(x,y):
    rectangle(x,y,30,10,0,(0,0,0))

def un(x,y):
    rectangle(x,y,10,100,0,(0,0,0))
    rectangle(x,y-15,-20,10,0,(0,0,0))

def deux(x,y):
    rectangle(x,y,50,10,0,(0,0,0))
    rectangle(x,y-90,50,10,0,(0,0,0))
    rectangle(x,y-50,50,10,0,(0,0,0))
    rectangle(x,y-90,10,-30,0,(0,0,0))
    rectangle(x+40,y-50,10,-40,0,(0,0,0))

def trois(x,y):
    rectangle(40+x,y,10,100,0,(0,0,0))
    rectangle(x,y,50,10,0,(0,0,0))
    rectangle(x,y-50,50,10,0,(0,0,0))
    rectangle(x,y-90,50,10,0,(0,0,0))

def cinq(x,y):
    rectangle(x,y,50,10,0,(0,0,0))
    rectangle(x,y,10,50,0,(0,0,0))
    rectangle(x,y-90,50,10,0,(0,0,0))
    rectangle(x+40,y-90,10,-30,0,(0,0,0))  
    rectangle(x,y-50,50,10,0,(0,0,0))

def six(x,y):
    rectangle(x,y,50,10,0,(0,0,0))
    rectangle(x,y,10,100,0,(0,0,0))
    rectangle(x,y-90,50,10,0,(0,0,0))
    rectangle(x+40,y-90,10,-30,0,(0,0,0))
    rectangle(x,y-50,50,10,0,(0,0,0))

Maintenant, que nous avons les lettres, il nous suffit d’y faire appel pour créer les noms.

Premièrement le F-16 :

def F_16(x,y):
    F(x,y)
    tiret(x,y-50)
    un(x+50,y)
    six(x+70,y)

Ensuite le F-22 :

def F_22(x,y):
    F(x,y)
    tiret(x,y-50)
    deux(x+40,y)
    deux(x+100,y)

Et dernièrement le F-35 :

def F_35(x,y):
    F(x,y)
    tiret(x,y-50)
    trois(x+40,y)
    cinq(x+100,y)

Les difficultés

La principale difficulté était de trouver les bonnes proportions et coordonnées à mettre pour positionner les formes.

Au début, une autre difficulté que nous avons rencontrée était d’utiliser des fonctions pour créer les avions, mais nous avons rapidement réussi.

L’IMAGE FINALE

Le fichier .py

Art

Five Night At Freddy’s 4

Pour notre premier projet en classe de 1ère spécialité NSI, nous nous sommes basés sur le thème du célèbre jeu vidéo d’horreur Five Night At Freddy’s 4. Notre image représente le menu du jeu vidéo Five Night At Freddy’s 4.


Ici vous trouverez les différentes étapes qui nous ont permis de créer cette image.

Description de notre projet.

Ce projet consiste a créer une image fait par l’art génératif grâce à un script python qui doit utiliser plusieurs fonctions. Pour ce projet, le thème de l’image créée est libre. Nous avons choisi de recréer l’image du menu du jeu vidéo d’horreur Five Night At Freddy’s 4 comme nous sommes dans la période d’&Halloween.

Pour le réaliser, nous avons utilisé le module turtle qui nous permet de tracer l’image.

Le fond de l’image.

Tout d’abord, nous avons commencé notre script par le code du fond de l’image. Ce code crée un dégradé du rouge jusqu’au noir, qui démarré à partir de la moitié de l’image pour aller jusqu’en haut. Il correspond au ciel.

for i in range(180):
    pencolor(120-int(i/2), 0, 0)
    forward(1280)
    up()
    backward(640)
    down()
    go(-640,i*2)

Nous avons choisi d’utiliser une boucle for i in range() : pour répéter 180 fois le programme. Le pencolor(120-int(i/2), 0, 0) permet de faire le dégradé. L’utilisation du -int fait en sorte que le fond va se dégrader moins rapidement et le (i/2) permet de régler la vitesse du dégradé.

Les nuages.

La seconde étape de notre projet consiste à créer des nuages. Ces nuages sont assez particuliers du fait de leur couleur qui se fond dans le dégradé du ciel.

def draw_stripes(x, y, radius, stripe_width):
    for i in range(-radius, radius, stripe_width):
        half_width = math.sqrt(radius**2 - i**2)

        up()
        goto(x - half_width, y + i)
        down()
        goto(x + half_width, y + i)

def cloud(x=0,y=0,r=30,s=2):#nuage
    pensize(0.1)
    pencolor(255,200,200)
    radius1 = r
    stripe_width = s
    for i in range(2):
        draw_stripes(x+30*i, y+10*i, radius1, stripe_width)
        draw_stripes(x+30*i, y-10*i, radius1, stripe_width)

Ici on a défini deux fonctions :

  • La première permet de faire des traits à l’intérieur d’un cercle pour régler l’opacité en turtle python.,
  • La seconde permet à la suite de la première d’empiler plusieurs cercles pour former un nuage.

La demi-lune.  

La troisième étape de notre script consiste à faire une demi-lune, qui se placera en haut à gauche de notre image.

def half_moon():
    fillcolor(255,255,255)
    pencolor(255,255,255)
    go(-500,200)
    begin_fill()
    left(180)
    circle(-60,180)
    left(200)
    circle(55,180)
    left(200)
    circle(-65,50)
    end_fill()
    left(190)

Ce script forme un demi-cercle plus grand puis un autre demi-cercle plus petit qui se rejoignent.

La maison.

La quatrième étape consiste à créer une maison qui se situera au milieu de notre image.

def House(t = 1):
    fillcolor(47,6,6)
    pencolor(47,6,6)
    go(0,35)
    for i in range(2):
        for i in range(2):
            begin_fill()
            forward(65*t)
            right(90)
            forward(15*t)
            right(90)
            end_fill()
        up()
        left(90)
        forward(30*t)
        right(90)
        down()
    go(0,35*t)
    for i in range(2):
        for i in range(2):
            begin_fill()
            forward(25*t)
            right(90)
            forward(25*t)
            right(90)
            end_fill()
        up()
        forward(40*t)
        down()
    go(130,95)
    for i in range(2):
        begin_fill()
        forward(40)
        right(90)
        forward(45*t)
        right(90)
        end_fill()
    begin_fill()
    go(170,95)
    left(105)
    forward(30*t)
    y=5
    for i in range(y):
        left(75/y)
        forward(2)
    forward(70*t)
    for i in range(y):
        left(90/y)
        forward(2)
    forward(35*t)
    backward(17*t)
    right(90)
    forward(40*t)
    y=10
    for i in range(y):
        left(90/y)
        forward(3)
    left(90)
    forward(55*t)
    backward(35*t)
    right(90)
    forward(15*t)
    left(90)
    forward(5*t)
    left(90)
    forward(15*t)
    end_fill()
    begin_fill()
    backward(15*t)
    left(90)
    forward(20*t)
    left(90)
    forward(25*t)
    left(90)
    forward(50*t)
    left(90)
    forward(25*t)
    left(90)
    forward(30*t)
    end_fill()

Dans ce script nous avons construit plusieurs rectangles grâce à des boucles for qui forment la maison.

Les arbres.

La cinquième étape, crée deux arbres qui sont constitués chacun de 6 points.

def tree(l=1,x=0,y=0):
    go(x,y)
    right(180)
    begin_fill()
    for i in range(2):
        forward(10*l)
        left(90)
        forward(30*l)
        left(90)
    left(90)
    forward(40*l)
    right(90)
    for i in range(2):
        dot(30*l)
        forward(5*l)
        dot(30*l)
        forward(5*l)
        left(90)
        dot(30*l)
        forward(15*l)
        dot(30*l)
        forward(15*l)
        left(90)
        dot(30*l)
    end_fill()

Ce code permet de créer 6 points qui vont faire les feuilles de l’arbre et un rectangle qui va faire le tronc.

Le sol.

Pour la sixième étape, nous avons fait le sol de notre image qui représente une colline.

begin_fill()
for i in range(400):
    forward(3)
    if i<35 or 80<i<100:
        ligne_down()

    elif 25<i<80 :
        ligne_up()

right(90)
forward(360)
right(90)
forward(1280)
right(90)
forward(347)
end_fill()

Le sol est fait par les fonctions ligne_up() et ligne_down() qui permettent de créer la montée et la descente de la colline.

Ecriture de FNAF4.

Pour la dernière étape nous avons fait créer le titre du jeu pour realiser l’intégralité de l’image du menu du jeu.  L’écriture Five Night At Freddy’s 4 sera de couleur rouge et se situera en bas à droite de notre image.

pencolor(200,10,10)
go(400,-200)
write('five',False,'left',('Baskerville Old Face',24,'normal'))
go(400,-230)
write('nights',False,'left',('Baskerville Old Face',24,'normal'))
go(400,-260)
write('at',False,'left',('Baskerville Old Face',24,'normal'))
go(400,-290)
write("freddy's",False,'left',('Baskerville Old Face',24,'normal'))
go(480,-320)
write("4",False,'left',('Baskerville Old Face',105,'normal'))

Pour écrire Five Night At Freddy’s 4, nous avons utilisé la fonction write() puis on a défini la police Baskerville Old Face avec la taille 24 et pour le chiffre 4 nous avons décidé de prendre la taille 105.

Les difficultés rencontrées durant le projet.

La première difficulté rencontrée et surtout celle qui nous a le plus posé problème est la création des nuages. Nous ne savions pas trop comment les coder et ni comment faire pour la couleur. Au début nous avons cherché une formule permettant d’avoir la couleur recherchée, que nous n’avons jamais trouvé. Finalement, à la suite de plusieurs essais, nous avons fait, un peu par hasard, une illusion d’optique avec l’assemblage de plusieurs traits qui donnent une couleur rouge plus claire.

Puis l’autre difficulté a été de maitriser les fillcolor(), car il fallait mettre au bon endroit le begin() et le end().

Les sources.

Pour écrire ce script nous avons utilisé nos connaissances ainsi que le site Python Docs pour trouver de nouvelles commandes. Nous avons aussi regardé quelques projets des années précédentes pour nous inspirer et nous guider.

Image finale.

Code complet.

Art

Un coucher de soleil à la Seyne-sur-Mer

Dans ce premier projet de première spécialité NSI, intitulé Art génératif, nous allons expliquer comment nous avons réalisé un programme représentant un coucher de soleil inspiré des paysages de La Seyne-sur-Mer, avec les célèbres rochers « Les Deux Frères ». Ce projet nous a permis d’explorer différents concepts de programmation en Python, en utilisant notamment les modules turtle et random pour donner un paysage le plus réaliste possible.

Origines de la Seyne-sur-Mer

La Seyne-sur-Mer est une commune française située dans le département du Var, en région Provence-Alpes-Côte d’Azur. Elle compte environ 65 000 habitants, ce qui en fait la deuxième ville la plus peuplée du Var, après Toulon.

Parmi ses nombreuses zones touristiques, on trouve les « Deux Frères », nom donné à deux rochers emblématiques de la côte maritime, émergeant à la pointe du Cap Sicié et visibles depuis les plages. Le nom « Deux Frères » provient d’une légende locale.

Le Projet

Pour réaliser ce projet, nous avons décidé de créer un paysage inspiré des couchers de soleil de La Seyne-sur-Mer. L’objectif était d’utiliser le plus de concepts vus en classe, tels que les modules Turtle et Random, ainsi que l’utilisation de boucles. De plus, pour donner un effet plus naturel, la position et la taille des nuages et des étoiles sont générées de façon aléatoire.

Structure du script

Notre script est divisé en plusieurs parties, chacune ayant une fonction distincte représentant un élément du paysage (dégradé, soleil, étoiles, nuages et les Deux Frères). Ces fonctions sont appelées à la fin dans un ordre spécifique pour créer l’image finale.

Analyse du script

L’analyse du script se déroule en plusieurs étapes.

1. Tout d’abord, nous importons les modules nécessaires, notamment le module Turtle pour le dessin graphique et le module Random pour générer des valeurs aléatoires.

from turtle import *
from random import randint

2. Fonction dégradé de couleur

# Fonction dégradé de couleur
def degrade(couleur_debut, couleur_fin, etapes, decalage_y, effet_vague=False):
    r_diff = couleur_fin[0] - couleur_debut[0]
    g_diff = couleur_fin[1] - couleur_debut[1]
    b_diff = couleur_fin[2] - couleur_debut[2]
    penup()
    goto(-640, decalage_y)
    pendown()

Cette fonction degrade prend quatre arguments : couleur_debutcouleur_finetapes, et decalage_y. Elle calcule la différence entre les valeurs RGB des couleurs de début et de fin, ce qui est nécessaire pour créer un dégradé. Ensuite, elle utilise penup() pour lever le stylo afin de ne pas dessiner pendant le déplacement, et goto() pour se positionner sur l’écran. pendown() est ensuite utilisé pour recommencer à dessiner. Cette partie est essentielle pour établir la base du dégradé.

3. Boucle pour réaliser le dégradé

    for i in range(etapes):
        if effet_vague:
            # Effet  vague si activé
            valeur_vague = int(30 * (1 + (i % 20) / 20.0))
            couleur_actuelle = (
                int(couleur_debut[0] + (r_diff * i / etapes)),
                int(couleur_debut[1] + (g_diff * i / etapes)),
                int(couleur_debut[2] + (b_diff * i / etapes + valeur_vague)),
            )
        else:
            # Couleur effet de vague desactivé 
            couleur_actuelle = (
                int(couleur_debut[0] + (r_diff * i / etapes)),
                int(couleur_debut[1] + (g_diff * i / etapes)),
                int(couleur_debut[2] + (b_diff * i / etapes)),
            )

Cette partie de la fonction utilise une boucle pour parcourir le nombre d’étapes spécifié. Dans la première branche de la condition if, si effet_vague est activé, un effet de vague est appliqué à la couleur actuelle en ajoutant une variation à la composante bleue. La couleur actuelle est ensuite calculée en ajoutant une autre couleur à celle de départ.

Dans le bloc else, qui s’exécute lorsque l’effet de vague est désactivé, la couleur actuelle est calculée sans cet effet.

4. La limite des valeurs pour les couleurs

        # Limite des couleurs (valeur compris entre 0 et 255) 
        couleur_actuelle = (
            min(255, max(0, couleur_actuelle[0])),
            min(255, max(0, couleur_actuelle[1])),
            min(255, max(0, couleur_actuelle[2])),
        )

Cette partie du script assure que les valeurs RGB de couleur_actuelle restent comprises entre 0 et 255, comme le montre l’utilisation de min et max. Cela permet de générer des couleurs valides.

5. Les lignes du dégradé

        pencolor(couleur_actuelle)
        forward(1280)
        goto(-640, decalage_y - (360 / etapes) * (i + 1))

Ici, la couleur du stylo est définie sur couleur_actuelle avec pencolor(couleur_actuelle). Ensuite, le curseur Turtle se déplace en avant sur 1280 pixels (forward(1280)), puis se repositionne pour dessiner la prochaine ligne du dégradé en fonction de l’étape actuelle.

6. Le soleil

# Fonction pour réaliser  le soleil
def soleil(position, rayon, couleur):
    penup()
    goto(position[0], position[1] - rayon)
    pendown()
    pencolor(couleur)
    fillcolor(couleur)
    begin_fill()
    setheading(90)
    circle(rayon, 180)
    end_fill()

Il n’y a pas de coucher de soleil sans le soleil. Nous avons donc créé la fonction soleil, qui dessine un demi-cercle à une position donnée et le remplit d’une couleur spécifiée.

7. Les étoiles

# Fonction pour réaliser  des étoiles dans le ciel disposer aléatoirement 
def etoiles():
    pensize(1)
    for _ in range(125):
        penup()
        x, y = randint(-700, 700), randint(0, 700)
        goto(x, y)
        pendown()
        couleur_etoile = (randint(230, 255), randint(230, 255), randint(230, 255))
        pencolor(couleur_etoile)
        dot(randint(1, 2))

Pour réaliser les étoiles, nous les avons disposées de façon aléatoire dans la partie supérieure, représentant le ciel. Nous avons également varié leur taille de manière aléatoire entre 1 et 2, afin d’ajouter un effet plus naturel au dessin. Ce processus pour les étoiles est réalisé 125 fois (for _ in range(125):)

8. Les nuages

# Fonction pour dessiner des nuages 
def nuages():
    penup()
    pencolor((200, 200, 200))  # Gris pastel
    for _ in range(15):
        x, y = randint(-600, 600), randint(100, 300)
        for _ in range(randint(5, 10)):
            offset_x = x + randint(-30, 30)
            offset_y = y + randint(-10, 10)
            goto(offset_x, offset_y)
            pendown()
            dot(randint(30, 60))
            penup()
            nouvelle_ombre = randint(190, 220)
            pencolor((nouvelle_ombre, nouvelle_ombre, nouvelle_ombre))
            for _ in range(randint(5, 10)):
                sous_offset_x = offset_x + randint(-20, 20)
                sous_offset_y = offset_y + randint(-10, 10)
                goto(sous_offset_x, sous_offset_y)
                pendown()
                dot(randint(10, 20))
                penup()

Pour réaliser les nuages, nous nous sommes inspirés d’une technique de dessin apprise en cours d’arts plastiques, consistant à dessiner plusieurs petits ronds et à les remplir avec trois nuances de gris différents. Les nuages sont disposés aléatoirement dans le ciel, avec des nuances de gris pastel, afin de rester cohérents avec le reste des couleurs du dégradé. Ils sont représentés par plusieurs points (dots) positionnés légèrement décalés pour créer une texture.

9. « Les Deux Frères »

# Fonction pour dessiner les "Deux Frères"
def les_deux_freres():
    penup()
    goto(200, 0)  # Position initiale pour les Deux Frères

    # Petit triangle à gauche
    fillcolor((140, 130, 120))
    begin_fill()
    pendown()
    setheading(60)
    forward(40)
    right(120)
    forward(40)
    right(120)
    forward(40)
    end_fill()
    penup()

    # Premier grand rocher
    goto(230, 0)
    fillcolor((120, 110, 100))
    begin_fill()
    pendown()
    setheading(60)
    forward(110)
    right(120)
    forward(110)
    right(120)
    forward(110)
    end_fill()
    penup()

    # Deuxième grand rocher 
    goto(310, 0)
    fillcolor((100, 90, 80))
    begin_fill()
    pendown()
    setheading(60)
    forward(120)
    right(120)
    forward(120)
    right(120)
    forward(120)
    end_fill()

Pour dessiner les Deux Frères, nous avons utilisé des triangles, représentant de manière stylisée les rochers. La couleur choisie est un gris sombre pour les rochers, afin de contraster avec le ciel. La position et l’orientation sont ajustées pour bien les intégrer dans le paysage.

10. Configuration de la fenêtre

# Configuration de la fenêtre Turtle
setup(1280, 720, 0, 0)
colormode(255)
speed(0)

Dans cette étapes du code nous délimitons la fenêtre afin d’avoir tous les éléments du décor dans la fenêtre afin de tous les apercevoir.

11. Execution des fonctions

# Création du coucher de soleil (océan + ciel)
degrade((0, 0, 255), (255, 255, 102), 720, 360)
degrade((255, 255, 102), (0, 0, 255), 720, 0, effet_vague=True)

# Dessin du soleil, des étoiles, des nuages et des îlots des Deux Frères
soleil((100, 100), 100, (255, 255, 102))
etoiles()
nuages()
les_deux_freres()
done()

Ici nous faisons appelle à tout les fonctions afin de réaliser le dessin.

Les difficultés rencontrées

Lors des nombreux tests réalisés, nous avons rencontré plusieurs difficultés, notamment avec les valeurs RGB et les fonctions dégradé et nuages, en raison de la complexité de leur création et des formules de calcul utilisées.

Source

Pour mener à bien ce projet, nous avons eu recours à nos connaissances personnelles ainsi qu’à Internet. Nous avons utilisé des sites comme NumWorks.com et Python.org pour apprendre de nouvelles choses, et nsi.xyz pour consulter les anciens projets afin de comprendre les attentes.

Télécharger le .py

L’image finale

Après beaucoup de tests et d’ajustements, voici le rendu final de notre projet que vous trouverez ci-dessous, résultat de tout le travail que nous y avons investi.

Projets

Linkin Park

Linkin Park est un groupe de rock et de métal américain formé en 1996. Il est actuellement composé de Emily Armstrong au chant à la suite du décès de Chester Bennington, Colin Brittain à la batterie à la suite du départ de Rob Bourdon, Mike Shinoda au chant(rap), à la guitare rythmique et au clavier, Brad Delson à la guitare solo, Dave Farrell à la guitare basse et Joe Hahn aux platines, effets et mixages.

Le fond

C’est un dégradé allant du blanc au noir grâce à une boucle for qui dessine les traits de couleurs au fur et à mesure. J’ai choisis ces teintes car le symbole et le nom du groupe sont habituellement représentés en noir avec un fond blanc ou inversement.

from turtle import *
colormode(255)
Screen()
setup(800, 600)
penup()
colormode(255)
speed(10)
goto(-400, -300)
pendown()

for i in range(600):
    pencolor(255 - round(i / (600 / 255)), 255 - round(i / (600 / 255)), 255 - round(i / (600 / 255)))
    forward(800)
    goto(-400,-300+i)

Le logo

Le logo du groupe Linkin Park a beaucoup changé au cours du temps et cette forme est sûrement ma préférée. Chaque angle de l’hexagone est censé symboliser un des membres de la bande (six au total). Pour le codage du logo ; il est composé d’un goto(x,y) pour définir où la figure commencera. Ensuite, la fonction circle() me servira deux fois pour les deux arc de cercle et enfin je ferais tous les angles à l’aide des fonction setheading() pour définir l’angle de direction du trait et forward() pour définir la longueur de ce trait.

pencolor(209,209,209)
pensize(10)
penup()
goto(0,250)
pendown()
circle(-100,-170)

setheading(70)
forward(145)

setheading(115)
forward(-90)

setheading(-180)
forward(108)

setheading(70)
forward(153)

setheading(0)
circle(-100,190)

L’écriture de Linkin

Pour écrire « Linkin » j’ai d’abord définit le i avec la fonction def pour le réutiliser dans d’autre lettres comme la barre du L, du N, du K et les deux barres du dernier N. J’ai ensuite utilisé les fonctions goto(x,y) pour commencer de nouvelles lettres aux coordonnées voulues, setheading() et forward() comme pour le logo. Mais j’ai aussi commencé à appliquer les fonction begin_fill() et end_fill() qui permette de remplir une portion de dessin de la couleur définit par fillcolor(r,g,b) tant que tous les pointes sont reliés.

penup()
goto(-240,20)
pendown()
# linkin park
# def i
def i(r,g,b,pensize):
    pencolor(r,g,b)
    fillcolor(r,g,b)
    
    begin_fill()
    
    setheading(0)
    forward(30)
            
    setheading(-90)
    forward(100)
    
    setheading(-180)
    forward(30)
    
    setheading(90)
    forward(100)
    
    end_fill()

# L
i(60,60,60,pensize(5))

setheading(-90)
forward(70)

fillcolor(60,60,60)
begin_fill()

setheading(0)
forward(70)

setheading(-90)
forward(30)

setheading(-180)
forward(70)

setheading(90)
forward(30)

end_fill()

penup()
goto(-155,20)
pendown()
# I
i(60,60,60,pensize(5))

penup()
goto(-110,20)
pendown()
#N
i(60,60,60,pensize(5))

penup()
goto(-80,20)
pendown()

begin_fill()

setheading(-55)
forward(60)

setheading(90)
forward(49)

setheading(0)
forward(30)

setheading(-90)
forward(100)

forward(50)

setheading(125)
forward(111)

setheading(90)
forward(59)

end_fill()

penup()
goto(-2,20)
pendown()
# K
i(60,60,60,pensize(5))

penup()
goto(28,-20)
pendown()

begin_fill()

setheading(50)
forward(52)

setheading(0)
forward(40)

setheading(-130)
forward(65)

setheading(-52)
forward(65)

setheading(-180)
forward(40)

setheading(130)
forward(52)

setheading(90)
forward(30)

end_fill()

penup()
goto(114,20)
pendown()
# I
i(60,60,60,pensize(5))

penup()
goto(158,20)
pendown()
# N
i(60,60,60,pensize(5))

penup()
goto(188,20)
pendown()

begin_fill()

setheading(-55)
forward(60)

setheading(90)
forward(49)

setheading(-90)
forward(100)

setheading(125)
forward(60)

setheading(90)
forward(50)

end_fill()

penup()
goto(222,20)
pendown()

i(60,60,60,pensize(5))

L’écriture de Park

Pour l’écriture de « Park », j’ai procédé de la même manière qu’avec « Linkin » : j’ai crée une fonction avec def qui a la même forme que la fonction i mais avec des mesures plus grandes et qui s’appelle donc I(i majuscule). J’ai réutilisé les fonctions circle() pour les boucles du P et du R ; c’est d’ailleurs le même bout de code grâce aux fonctions setheading() et forward() (on aurait été de changer toutes les données avec des goto(x,y). Le K, lui, est le même que le petit k mais avec des mesures plus grandes. Pour l’intérieur du A j’ai fais un triangles avec les fonctions setheading() et forward() puis j’ai essayé de le remplis avec begin_fill() et end_fill() de la même couleur que le fond.

penup()
goto(-240,-90)
pendown()
# park
# def I
def I(r,g,b,pensize):
    pencolor(r,g,b)
    fillcolor(r,g,b)
    
    begin_fill()
    
    setheading(0)
    forward(40)
            
    setheading(-90)
    forward(130)
    
    setheading(-180)
    forward(40)
    
    setheading(90)
    forward(130)
    
    end_fill()

# P
I(0,0,0,pensize(5))

penup()
goto(-200,-90)
pendown()

begin_fill()

setheading(0)
forward(30)
circle(-40,180)
setheading(-180)
forward(30)

setheading(90)
forward(29)
setheading(0)
forward(10)
circle(12,180)
setheading(-180)
forward(10)
setheading(90)
forward(27)

end_fill()

penup()
goto(-138,-90)
pendown()
# A

begin_fill()

setheading(0)
forward(85)

setheading(-75)
forward(135)

setheading(-180)
forward(45)

setheading(112)
forward(20)

setheading(-180)
forward(26)

setheading(-115)
forward(20)

setheading(-180)
forward(45)

setheading(70)
forward(105)

setheading(130)
forward(41)

end_fill()

penup()
goto(-91,-180)
pensize(2)
pencolor(219,219,219)
fillcolor(219,219,219)
pendown()

begin_fill()

setheading(0)
forward(15)

setheading(101)
forward(40)

setheading(-101)
forward(40)

end_fill()

penup()
goto(-2,-90)
pencolor(0,0,0)
fillcolor(0,0,0)
pendown()
# R

I(0,0,0,pensize(5))

penup()
goto(38,-90)
pendown()

begin_fill()

setheading(0)
forward(30)
circle(-40,180)
setheading(-180)
forward(30)

setheading(90)
forward(29)
setheading(0)
forward(10)
circle(12,180)
setheading(-180)
forward(10)
setheading(90)
forward(27)

end_fill()

penup()
goto(70,-165)
pendown()

begin_fill()

setheading(-60)
forward(67)

setheading(-90)
forward(60)

setheading(122)
forward(130)

setheading(90)
forward(26)

setheading(0)
forward(27)

end_fill()

penup()
goto(120,-90)
pendown()
# K

I(0,0,0,pensize(5))

penup()
goto(130,-175)
pendown()

begin_fill()

setheading(50)
forward(110)

setheading(0)
forward(50)

setheading(-130)
forward(80)

setheading(-52)
forward(80)

setheading(-180)
forward(50)

setheading(130)
forward(115)

setheading(90)
forward(30)

end_fill()

Image finale

Télécharger le script en .py :

Voici le script complet !

Projets

Une nuit étoilée

Pour notre premier projet de la spé NSI dans le lycée Louis Pasteur, on nous à demandé de réaliser une image déssinée en Python. Nous avions libre choix de réaliser le dessin que l’on desirait, on à donc décidé de réaliser un sujet qui nous passione – le Pixel Art. Seulement, déssiner un pixel art en python demande beaucoup de travail et de réflexion.

Pour valoriser encore plus notre travail, nous avons décidé de rendre cette image animée.

C’est pour cela que le prof nous à suggeré d’utiliser les fonctions, et dans la suite de cet article, nous vous présenterons les techniques qui nous ont permi d’optimiser et de réaliser notre animation représentant un Pixel Art d’une nuie étoilée.

Schéma Original

Tout d’abord, Léandre à commencé par schématiser notre idée sur un logiciel de déssin pixelart – Aseprite.

Nous avons décidé de transcrire seulement les rideaux et la fenêtre en python, sinon le travail était trop manuel, long, et ne demandait pas tant de réfléxion.

Reconstitution des rideaux en Python

Commençons par le commencement :

Nous avons défini les constantes suivantes, qui vont nous aider plus tard.

SCREEN_WIDTH = 1280 + 10
SCREEN_HEIGHT = 720 + 10

PIXEL_SIZE = 15.25

SNOWFLAKE_COUNT = 50
SNOWFLAKE_SIZE_RATIO = 0.1
SNOWFLAKE_MOVE_DOWN = -10

STAR_COUNT = 100

WAIT_TIME_MS = 100
TOTAL_GENERATIONS = 100

Puis, nous avons transcrit manuellement le schéma en python à l’aide de boucles for, et d’une fonction spéciale, conçue par Clovis, nommée draw_pixel_symmetric(couleur, x, y).

Pourquoi symmetric? Car cela nous permet de réaliser 2 rideaux en même temps, symétrique à l’axe vertical, en 2 fois moins de lignes !

D’ailleurs, fun fact. Nous avions pris plus d’une heure à trouver comment réaliser cette symétrie. Clovis était parti dans l’écriture d’un algorithme de dessin de pixels sous forme de dictionnaires {"y = 0": ["Couleur du pixel en x = 0", "Couleur du pixel en x = 1", "etc.."], "y 1":["..."]} avant de trouver une solution plus simple, efficace qui occupe un peu moins de 10 lignes.

def draw_pixel_symmetric(couleur, x, y):
    # OG PIXEL
    penup()
    goto(x, y)
    pendown()
    color(couleur)
    begin_fill()
    for _ in range(4):
        forward(PIXEL_SIZE)
        left(90)
    end_fill()
    penup()

    # SYMMETRIQUE PIXEL
    goto(-x, y)
    pendown()
    begin_fill()
    for _ in range(4):
        forward(PIXEL_SIZE)
        left(90)
    end_fill()
    penup()

C’est évident – au lieu d’avoir un seul pixel en x, y, nous l’avons dupliqué et placé le doublon en -x, y.

Seulement, si c’était si simple.. Nous avons crée la fonction rideaux() où se focalise la transcription manuelle. Merci à Léandre d’avoir transcrit l’entièreté du rideau (2h de travail : 1400 lignes de code).

def rideaux():
    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2 + PIXEL_SIZE
    
    for _ in range(2):
        draw_pixel_symmetric('#9e1b1b',x,y)
        x = x + PIXEL_SIZE

    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2 + PIXEL_SIZE * 2

    draw_pixel_symmetric('#9e2828',x,y)
    x = x + PIXEL_SIZE

    draw_pixel_symmetric('#ac3232',x,y)
    x = x + PIXEL_SIZE
    
    # ... Et la suite dont on vous fait part d'abstraction.
    

rideaux() sans système de symétrie

rideaux() avec draw_pixel_symmetric(). Et voici donc le résultat final. Pas mal non ? Ce n’est seulement que la première étape.

Système de gradient

Le système de gradient, développé par Clovis, prend 5 arguments :

  • colors (liste ["couleur 1", "couleur 2"])
  • x_start
  • y_start
  • x_end
  • y_end

Sans oublier une fonction qui converti l’héxadécimal en décimal, pour pouvoir manipuler des couleurs RGB, et assurer une transition des couleurs de manière fluide.

def hex_to_rgb(hex_color):
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))

def draw_gradient(colors, x_start, y_start, x_end, y_end):
    penup()
    setheading(0) # tortue regarde à l'est
    current_y = y_start
    total_steps = y_end - y_start

    start_rgb = hex_to_rgb(colors[0])
    end_rgb = hex_to_rgb(colors[1])

    while current_y <= y_end:
        proportion = (current_y - y_start) / total_steps
        interpolated_rgb = tuple(
            int(start + (end - start) * proportion) 
            for start, end in zip(start_rgb, end_rgb)
        )
        pencolor(interpolated_rgb)
        penup()
        goto(x_start, current_y)
        pendown()
        forward(x_end - x_start)
        current_y += 1

Expliquons la fonction draw_gradient()plus en détail :

Celle-ci effectue une boucle while qui s’arrête lorsque current_y dépasse ou est égal aux coordonnées y de destination (y_end).

Pour calculer la valeur RGB correspondante à la position de current_y, nous effectuons une boucle for qui consiste à construire un tuple contenant les valeurs rouge, vert, et bleu en fonction de start_rgb, end_rgb et la valeur qui change selon current_y : proportion.

Résultat avec les valeurs hexadécimales ["#4a4c9b", "#050758"].

Appel de la fonction complète : draw_gradient(["#4a4c9b", "#050758"], -SCREEN_WIDTH//2, -SCREEN_HEIGHT//2, SCREEN_WIDTH//2, SCREEN_HEIGHT//2)

Les étoiles

Pour pimenter notre gradient, nous avons placé des étoiles à des emplacements aléatoires.

Voici l’algorithme que nous avions développé.

def initialize_positions(count, screen_width, screen_height):
    # liste avec des tuples dedans PRCQ ON ADORE LES TUPLES !!!!! TOUCHE PAS A MON TUPLE !!!
    positions = []
    for _ in range(count):
        positions.append((randint(-screen_width // 2, screen_width // 2), randint(-screen_height // 2, screen_height // 2)))
    
    return positions

def draw_stars(stars):
    for (x, y) in stars:
        draw_pixel(2, x, y, '#ffffff')

stars = initialize_positions(STAR_COUNT, SCREEN_WIDTH, SCREEN_HEIGHT)
draw_stars(stars)

Pour générer les positions aléatoires, on à décidé de créer une fonction initialize_positions(count, screen_width, screen_height) qui prend en compte les arguments correspondant au montant désiré, la largeur et la hauteur de l’écran. Cette fonction renvoie une liste [] remplie de tuples (x, y) contenant les positions aléatoires en x et y. Créer cette fonction nous permettra d’optimiser encore plus notre système de flocons dans la prochaine section de cet article.

Puis, une fonction draw_starts(stars) à été réalisée pour looper dans chaques tuples contenus dans la liste stars et placer un pixel de taille 2 à l’emplacement correspondant.

Gradient + Etoiles

Gradient + Etoiles + Rideaux

Les compléments des rideaux

Pour ajouter un peu plus de détails, nous avons décidé de rajouter un mur en bas de l’image et un cadre de fenêtre.

De plus, nous avons détaillé les couleurs en rajoutant un système de randomization pour altérner les couleurs marron (claires & foncées). Pour faire cela, il fallait inventer un système pour que chaques pixels ai une variation de marron propre à celui-ci. (Vu qu’il y a une animation plus tard, faire colour_variations_marron[randint(0, 1)] changerait la couleur du pixel à chaques générations).

colour_variations_marron = []
# initialisation des variations de couleurs une seule fois
colour_variations_marron = tuple(
    tuple("#63352d" if randint(0, 1) == 0 else "#5e3129" for _ in range(SCREEN_HEIGHT))
    for _ in range(SCREEN_WIDTH)
)
def supplement_rideaux():

    x = -SCREEN_WIDTH//2 
    y = SCREEN_HEIGHT//2

    # Barre horizontale

    for _ in range(44):
        draw_pixel_symmetric('#4e2b25',x,PIXEL_SIZE * 2.2)
        x = x + PIXEL_SIZE

    x = 0 - PIXEL_SIZE * 12
    y = SCREEN_HEIGHT//2

    # Double Barre Verticale

    for _ in range(45):
        color = colour_variations_marron[round(x)][round(y)]
        draw_pixel_symmetric(color, x, y)
        draw_pixel_symmetric(color, x - PIXEL_SIZE // 1.5, y)
        y -= PIXEL_SIZE

    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2 + PIXEL_SIZE * 8
    
    # Barre horizontale au dessus du mur

    for _ in range(43):
        draw_pixel_symmetric('#4e2b25',x,y)
        x = x + PIXEL_SIZE
    
    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2

    # Mur en bas

    for _ in range(7):
        for _ in range(22):
            for _ in range(3):
                color = colour_variations_marron[round(x)][round(y)]
                draw_pixel(PIXEL_SIZE,x,y,color)
                x = x + PIXEL_SIZE
            
            draw_pixel(PIXEL_SIZE,x,y,'#8f563b')
            x = x + PIXEL_SIZE
        x = -SCREEN_WIDTH//2
        y += PIXEL_SIZE
        

Et voici le résultat !

Les flocons animés

Cette dernière partie était la plus intéressante à faire. Beaucoup de travail et de recherches mais pour un résultat surprenant.

Voici la fonction draw_snowflake() qui déssine un flocon en x, y. Le choix d’une couleur est possible grâce à l’argument couleur. Et la taille de celui-ci est désirable grâce à l’argument size_ratio. Étonnement, Clovis était celui qui à désinné ce flocon, et à trouvé cela plus compliqué comparé aux autres fonctions développées dans tout le projet (ça à pris du temps et de l’imagination).

def draw_snowflake(size_ratio, og_x, og_y, couleur):
    color(couleur)
    x, y = og_x, og_y
    ps = 7 * size_ratio
    # pixel(ps, og_x, og_y)

    goto(og_x - 8*ps, og_y - 8*ps)
    x, y = pos()
    
    left(90)
    pendown()

    for _ in range(16):
        draw_pixel(ps, x, y)
        x, y = x + ps, y + ps
        goto(x, y)

    goto(og_x - 8*ps, og_y + 7*ps)
    x, y = pos()

    for _ in range(16):
        draw_pixel(ps, x, y)
        x, y = x + ps, y - ps
        goto(x, y)

    goto(og_x - ps, og_y + ps * 8)
    x, y = pos()
    og_x2, og_y2 = x, y

    for p in range(18):
        goto(og_x2, og_y2 - (p * ps))
        x, y = pos()
        for i in range(2):
            draw_pixel(ps, x + (i * ps), y)
            x, y = pos()

    goto(og_x - 8 * ps, og_y- ps)
    x, y = pos()
    og_x2, og_y2 = x, y

    for p in range(16):
        goto(og_x2 +  (p * ps), og_y2)
        x, y = pos()
        for i in range(2):
            draw_pixel(ps, x , y+ (i * ps))
            x, y = pos()
    penup()

Quel banger – Léandre quand Clovis à montré son flocon pour la premère fois

Ensuite, passons au système de mouvement des flocons.

def update_snowflake_positions(current_positions):
    updated_positions = []
    for (x, y) in current_positions:
        new_y = y + SNOWFLAKE_MOVE_DOWN
        # snowflake pos reset if goes too down
        if new_y < -SCREEN_HEIGHT // 2:
            new_y = SCREEN_HEIGHT // 2
            new_x = randint(-SCREEN_WIDTH // 2, SCREEN_WIDTH // 2)
            updated_positions.append((new_x, new_y))
        else:
            updated_positions.append((x, new_y))
    return updated_positions

snowflakes = initialize_positions(SNOWFLAKE_COUNT, SCREEN_WIDTH, SCREEN_HEIGHT)

Pour commencer, la fonction update_snowflake_positions(current_positions) prend en compte la liste [(x, y), (x, y)] contenant les tuples de positions x, y de chaques flocons. (réf. initialize_positions(count, screen_width, screen_height)).

Puis initialise une nouvelle liste updated_positions qui contient une nouvelle version de chaques tuples (x, y), cette fois-ci avec une valeur y différée selon la constante SNOWFLAKE_MOVE_DOWN (new_y = y + SNOWFLAKE_MOVE_DOWN).

En plus, nous avons ajouté un système qui replace le flocon en haut de l’écran si les coordonnées y de celui-ci dépasse le point le plus bas de l’écran.

  • Point le plus haut de l’écran : SCREEN_HEIGHT // 2
  • Point le plus bas de l’écran : -SCREEN_HEIGHT // 2

La valeur SCREEN_HEIGHT est divisée par 2, car le module turtle fait en sorte que la position (0, 0) correspond au centre absolu de l’écran.

Enfin, voici la boucle finale permettant une animation.

for generation in range(1, TOTAL_GENERATIONS + 1): # main loop
    clear()

    snowflakes = update_snowflake_positions(snowflakes)
    draw_snowflakes(snowflakes)

    update()

    print(f"Generation n'{generation} dessinée :D")

    time.sleep(WAIT_TIME_MS / 1000)  # Converti millisecondes en seconds

Explication des termes :

  • clear() permet d’effacer complètement tous les éléments de la fenêtre turtle.
  • update() permet de mettre à jour la fenêtre, utile pour réaliser des animations.
  • time.sleep(WAIT_TIME_MS / 1000) permet un temps d’attente entre chaques image exposée. Ici la constante WAIT_TIME_MS est de 100, et la fonction time.sleep() ne prend seulement comme argument des secondes. Il faut donc convertir les millisecondes en secondes, signifiant une division par 1000.

Si TOTAL_GENERATIONS = 1, alors turtle nous renverra une seule image, et non un animé.

Autrement, si TOTAL_GENERATIONS est au dessus de 1, on obtient une animation. Voici un exemple avec 100 :

Pas mal non ?

Sources

Pour réaliser toutes ces fonctions, nous avons utilisé nos propres connaissances, logique, et w3schools qui nous à beaucoup servi pour comprendre certaines opérations et fonctions integrées dans python (comme la fonction zip).

Image et animation finale

Télécharger le projet

Remarque des enseignants : les élèves n’ont pas respecté le nombre maximal de lignes imposé (404) ni la charte de nommage des variables. Cela pose de nombreux problèmes. L’article est néanmoins publié.

Art

De l’Op art généré par un code python

L’Op Art, ou art optique est un mouvement artistique qui joue avec notre perception visuelle. Son origine remonte aux années 1920 mais il connaît un véritable essor dans les années 1960 avec des artistes comme Victor Vasarely et Bridget Riley. Ils utilisent notamment les contrastes de couleurs, la perspective, la répétition et l’alternance de formes géométriques, pour créer l’illusion de mouvement et de profondeur. Dans cet article, nous allons vous présenter une œuvre inspirée de ce mouvement et entièrement réalisée à l’aide d’un code écrit en langage Python avec le module Turtle.

L’inspiration de départ

Le problème avec les illusions d’optique est que ce que l’on voit, n’est pas la réalité. La première difficulté rencontrée a donc été de trouver un modèle avec des explications pour réaliser la figure. Après de nombreuses recherches, nous avons fini par trouver un programme de construction d’un couloir en damier sur le site d’une classe de CM2 (https://la-gazette-des-cm2-de-ducerceau.sitew.fr/Illusions_d_optique.qB.htm) dans le cadre d’un travail sur Victor Vasarely. Nous l’avons adapté pour respecter les dimensions de l’image finale (forme rectangulaire et non carrée).

Le programme de construction

Le programme de construction nous a permis d’identifier les différentes étapes à programmer :

  • Partager les côtés du rectangle en intervalles de même longueur ;
  • Tracer les segments qui relient deux points symétriques par rapport à l’origine ;
  • Tracer les rectangles qui diminuent régulièrement en taille.
  • Pour apporter notre touche personnelle, nous avons choisi de « peindre » les murs, le sol et le plafond du « couloir » avec des dégradés de couleurs.

Les variables modifiables seront la longueur, la largeur, les couleurs, le nombre d’intervalles, la taille des intervalles, le nombre de rectangles.

Programmation des couleurs

Nous avons commencé par les gradients de couleur en utilisant la méthode expliquée par Eric Schrafstetter dans sa vidéo « l’ART génératif, partie 1 – Transformations affines. »

Explication pour le « mur droit » :

  • La tortue se déplace en abscisse de 0 à L/2. à chaque pas, elle va tracer un segment en couleur de bas en haut (cap = 90°).
  • Pour une abscisse u donnée, ce segment part d’une diagonale et arrive sur l’autre. Donc on a créé les fonctions diag1 et diag2 qui retournent les ordonnées sur chaque diagonale.

def diag1(u): 
    return largeur/longueur * u
def diag2(u):
    return - largeur/longueur * u
  • Entre 0 et L/2, la couleur varie de col_dep (couleur de départ = violet) à col_arr (couleur d’arrivée = jaune).
  • Comme la couleur ne varie pas sur le même intervalle que l’abscisse de la tortue, on utilise une fonction affine qui transforme un point de l’intervalle [0 ; L/2] en un point de l’intervalle [col_dep ; col_arr] :

Voici la fonction codée en Python :

def T(u, a1, a2, b1, b2): 
    return (b2 - b1)/(a2 - a1) * (u - a1) + b1
  • Comme les dégradés ne sont pas dans le même sens ni de la même couleur suivant les triangles, nous avons créé une fonction « triangle » qui dépend de l’orientation de la tortue (cap) et des couleurs de départ et d’arrivée :
def triangle (cap, col_dep, col_arr): 
    colormode(255) # pour pouvoir utiliser le mode RVB
    rvb = [0,0,0]
    pensize(2)
    u_fin= trunc(longueur/2)
    for u in range(u_fin):
        for k in range(3):
            rvb[k] = T(u, 0, u_fin, col_dep[k], col_arr[k])
        pencolor((trunc(rvb[0]), trunc(rvb[1]), trunc(rvb[2])))
        penup()
        if cap == 90 :
            goto(u,diag2(u))
            pendown()
            goto(u,diag1(u))
        elif cap == 180 :
            goto(u,diag1(u))
            pendown()
            goto(-u,diag2(-u))
        elif cap == 270 :
            goto(-u,diag2(-u))
            pendown()
            goto(-u,diag1(-u))
        elif cap == 0 :
            goto(-u,diag1(-u))
            pendown()
            goto(u,diag2(u))

Programmation des « diagonales »

Pour simplifier, nous avons appelé aussi « diagonales », les segments qui relient un point et son symétrique par rapport à l’origine.

La tortue part de (-L :2 ; -l /2) à gauche, fait sa diagonale, revient à gauche en se décalant d’un intervalle vers le haut, fait sa diagonale, …

On procède de la même manière pour les diagonales de haut en bas, en partant de (-L/2 ; l/2).

On a donc d’abord créé une fonction « diag » qui sert à relier un point et son symétrique :

def diag(u, v):
    penup()
    goto(u, v)
    pendown()
    goto(-u, -v)
    penup()

Puis, nous avons programmé deux boucles, pour tracer les  « diagonales gauche-droite » puis les « diagonales haut-bas » :

for i in range(nb_int2):
    diag(-longueur / 2, -largeur/2 + i*taille_int2)
for j in range(nb_int1):
    diag(-longueur/2 + j*taille_int1, largeur / 2)

Programmation des rectangles

Nous avons codé une fonction « rectangle » où la tortue part du coin bas à gauche et tourne dans le sens inverse des aiguilles d’une montre :

def rectangle(L): # fonction permettant de tracer un rectangle
    penup()
    goto(-L/2, diag1(-L/2))
    pendown()
    goto(L/2,diag2(L/2))
    goto(L/2,diag1(L/2))
    goto(-L/2,diag2(-L/2))
    goto(-L/2, diag1(-L/2))

Pour faire diminuer la taille des rectangles de manière régulière, nous avons décidé d’appliquer un pourcentage de diminution de p% à la longueur (p est une variable globale qui peut être modifiée) :

t = float(longueur) 
while t > 10 :
    rectangle(t)
    t = t - t*p #la longueur diminue de p % à chaque itération

Pour l’extrémité du couloir, nous avons tracé un rectangle noir pour lequel nous avons essayé plusieurs dimensions et nous avons finalement choisi 160 pour des raisons esthétiques :

# rectangle final
fillcolor('black')
begin_fill()
rectangle(160)
end_fill()

Le programme zippé