Catégorie : Art

Art

Paysage : Le coucher de soleil en Python

Pour notre premier projet de NSI, qui a pour sujet l’art génératif, nous avons décidé de créer une image d’un coucher de soleil en bord de mer, en essayant de capitaliser un maximum sur l’ambiance générale de l’image, pour la rendre intéressante et détaillée. Cet article va vous présenter point par point le processus de création de ce projet. Bonne lecture !

La vidéo de présentation du projet :

Les autres pistes du projet

De nombreuses pistes ont été explorées quant à quoi faire comme projet de NSI. Il nous fallait trouver quelque chose de complexe, mais pas infaisable, ainsi que parlant et pas complètement hors sujet. 

Nous d’abord pensé à faire une ville futuriste, tirée du jeu vidéo Cyberpunk 2077, mais l’idée a été abandonnée de peur de ressembler au Paysage Synthwave. Puis nous nous sommes tournés vers la musique et à des albums tels que In Rainbows de Radiohead, In Absentia de Porcupine Tree, ou encore Ænima de TOOL, mais ces possibilités ont été rejetées de peur de ne pas être à la hauteur de l’œuvre originale.

Alors, nous nous sommes aventurés dans les anciens projets d’élèves avant de tomber sur l’idée d’un paysage et enfin de la mer, car c’est un environnement qui évoque à la fois de la nostalgie et de la joie  et qui parlera donc au plus grand nombre, mais qui représente également un certain challenge à représenter et donc, l’idée idéale.

Déroulement du projet

Ce projet a été conçu sur les deux semaines des vacances de la Toussaint 2024, avec les 3/4 du temps entièrement consacré au code et l’image, et les quelques derniers jours à la réalisation de cet article, ainsi que de la vidéo. Nous avons utilisé des IDE tels que Thonny et VS Code, ainsi que de l’outil en ligne Replit, afin de partager notre code, et tester nos fonctions.

Les étapes de création de notre image

Afin d’avoir une meilleure idée des couches et des objets dont notre image allait avoir besoin, nous avons commencé ce projet par un brouillon. Et bien que l’image finale ne ressemble en rien à ce qui était prévu, cela nous a quand même permis de commencer notre code et d’en construire la structure et les fonctions par la suite.

Une des premières choses que nous avons faite est une fonction essentielle de notre code, nommée go() et qui permet à la turtle de se « téléporter » sans laisser de trace, ce qui s’avèrera très utile pour la lisibilité du code, en plus d’économiser des lignes.

def go(x, y) :
    penup()
    goto(x, y)
    pendown()

Le Ciel

Après cela nous avons créé notre fonction ciel(), sûrement une des plus complexes du code, qui se compose de trois boucles. La première créée un fond ayant un dégradé de couleurs rappelant un ciel lors d’un coucher de soleil. Ici, il a été difficile de trouver une bonne manière de faire un dégradé qui prenne bien en compte nos deux couleurs, et beaucoup d’essai ont été nécessaires pour trouver une bonne formule.

Ensuite, la seconde fait apparaitre des étoiles avec le module random. Enfin, ainsi que des raies de couleur. L’utilisation de la fonction randint() du module random permet de faire en sorte que les étoiles sont à des positions quasiment toujours différentes.

Enfin, la dernière boucle a été trouvée de manière complètement accidentelle. En faisant des recherches pour la formule de la première boucle, nous nous sommes rendu compte qu’espacer l’ordonnée du retour « à la ligne », à la ligne 15, permettait des stries dans le ciel, ce qui constitue au final un détail important de notre image.

def ciel(col1, col2, larg, long) :

    colormode(255)
    h = -long / 2 + long // 6
    go(-larg / 2, h)

    for i in range(long) :

        r = col1[0] + (col2[0] - col1[0]) * i / long 
        g = col1[1] + (col2[1] - col1[1]) * i / long
        b = col1[2] + (col2[2] - col1[2]) * i / long

        pencolor(int(r), int(g), int(b))
        forward(larg)
        go(-larg / 2, h+i)

    for i in range(300) :

        x = randint(-640, 640)
        y = randint(-360, 360)
        taille = randint(1, 4)

        c = randint(200, 240)
        color(c,c,c)

        go(x, y)
        dot(taille)

    for i in range(long) :

        r = col1[0] + (col2[0] - col1[0]) * i / long
        g = col1[1] + (col2[1] - col1[1]) * i / long
        b = col1[2] + (col2[2] - col1[2]) * i / long

        pencolor(int(r), int(g), int(b))
        forward(larg)
        go(-larg / 2, h+i*10)


Le résultat de cette fonction est le suivant :

La Mer

La fonction de la mer est plus ou moins la même que celle du ciel, mais avec des valeurs différentes, retournant le dégradé, et le plaçant bien plus bas.

def mer(col1, col2, larg, long) :

    h = -270
    go(-larg / 2, h)

    for i in range(long) :

        r = col1[0] + (col2[0] - col1[0]) * i / long
        g = col1[1] + (col2[1] - col1[1]) * i / long
        b = col1[2] + (col2[2] - col1[2]) * i / long

        pencolor(int(r), int(g), int(b))
        forward(larg)
        go(-larg / 2, h+i)


On obtient alors ce dégradé, bien plus comprimé que le précédent, mais dont la partie inférieure est principalement cachée par les reliefs.

Le Soleil

Le Soleil a sûrement été la fonction la plus bizarre à créer de ce projet. En effet, bien qu’elle soit assez courte, trouver la bonne formule pour les couleurs des anneaux a demandé beaucoup d’essai sans trop savoir où aller, comme pour le ciel et son dégradé de couleur.

C’est après avoir créé un cercle central qu’elle change les couleurs et repart d’un point plus bas pour tracer un cercle plus grand, toujours de la même valeur (i*10) et que nous avons simplifié sous la forme d’une variable t, pour des questions de lisibilité.

def soleil(x, col) : 

    go(0, -250)
    color(col)
    begin_fill()
    circle(x)
    end_fill()

    for i in range(5) :

        t = i * 10

        r = max(col[0] - t, 0)
        g = max(col[1] - t * 3, 0)
        b = 100 - t * 2

        color(r, g, b)
        width(12)
        go(0, -250 - t)
        circle(x + t)

Nous avons aussi rajouté des effets sur la mer qui représente les reflets du soleil. Pour cela nous avons utilisé une boucle ainsi qu’une structure conditionnelle, dans une nouvelle fonction, appelée reflets(), qui est assez simple et qui sera par ailleurs réutilisée plus tard pour les reflets du bateau :

def reflets(x, y, larg, col, taille, n) :

    setheading(0)

    for i in range(n) :
        if n==7 :
            pencolor(col)
            pensize(taille)
            go(x - larg // 2, y - i * 20)
            forward(larg)
            larg -= 50 * i / 2
            taille -= 2
        else : 
            pencolor(col)
            pensize(taille)
            go(x - larg // 2, y - i * 8)
            forward(larg)
            larg -= 50 * i / 2
            taille-=1


On obtient alors ce résultat, de notre soleil sans mer en dessous :

La Plage

Pour faire la plage nous avons créé une fonction nommée plage où nous y avons créé une autre fonction nommée arrière_plan. La fonction fait se téléporter la tortue grâce à la fonction go() à des coordonnées qui sont contenues dans une liste, qui fut très laborieuse à faire (comme celles à venir). Ensuite, grâce aux fonctions begin_fill() et end_fill(), la forme créée est remplie d’une couleur contenue dans les structures conditionnelles.

def plage(x, y) :

    def arrière_plan(x, y) :

        go(x, y)
        color(40, 25, 25)

        points = [(x, y + 190),(x + 200, y + 150), (x + 250, y + 125), (x + 500, y + 100), (x + 642, y + 100), (x + 800, y + 80), (x + 1280, y+ 130), (x + 1280, y)]

        begin_fill()
        for x, y in points:
            go(x, y)
        end_fill()


    arrière_plan(x, y+75)

    go(x, y)
    begin_fill()

    for i in range(60) :

        pensize(10)
        color(28, 22, 19)
        forward(1280)
        y += 2
        go(x, y)

Les Reliefs

Pour créer cet effet de profondeur sur la plage nous avons utilisé une fonction utilisant le même principe que la fonction plage(). Le code reste très proche de l’autre fonction avec quelques différences. Par exemple, le paramètre n permet de choisir quelles listes, couleur et coordonnées le code choisi, afin de ne pas avoir à recréer des fonctions inutilement. Puis, le code parcours chaque point dans l’ordre et remplit la forme.

def reliefs(x, y, n) :

    if n == 1 :
        x,y = -640, -250
        go(x, y)
        color(28, 22, 19)

        points = [(x, y + 150), (x + 100, y + 100), (x + 200, y + 50), (x + 300, y + 30), (x + 400, y + 70), (x + 450, y + 60), (x + 475, y + 50), (x + 500, y + 40), (x + 600, y + 10), (x + 642, y + 20), (x + 700, y + 30), (x + 800, y + 30), (x + 900, y + 30), (x + 1000, y + 50), (x + 1100, y + 70), (x + 1200, y + 100), (x + 1280, y + 150), (x + 1280, y)]

        begin_fill()
        for x, y in points:
            go(x, y)
        end_fill()

    if n == 2 :
        x,y = -640, -400
        go(x, y)
        color(20, 14, 11)

        points = [(x, y + 250), (x + 200, y + 150), (x + 600, y + 125), (x + 900, y + 175), (x + 1200, y + 200), (x + 1280, y + 150), (x + 1280, y)]

        begin_fill()
        for x, y in points:
            go(x, y)
        end_fill()

    if n == 3 : 
        x,y = -640, -360
        go(x, y)
        color(28, 19, 14)

        points = [(x, y + 150),(x+250, y + 100),(x+750, y + 50), (x + 1280, y+150), (x + 1280, y)]

        begin_fill()
        for x, y in points:
            go(x, y)
        end_fill()

Nous l’avons appelé un total de 3 fois, créant ainsi une profondeur plus conséquente, même sans perspective et points de fuites concrets dans l’image.

reliefs(-640,-250, 1)
reliefs(-640,-400, 2)
reliefs(-640, -360, 3)


Avec la fonction plage() et des fonctions reliefs() combinées on obtient ce résultat :

Les Palmiers

Jusqu’ici, notre plage au bord de la mer ressemblait plus à un amas de rocher sans vie qu’au résultat escompté. Ainsi, pour donner un côté plus exotique à notre image nous avons décidé de rajouter des palmiers. Ils sont créés à l’aide de 2 fonctions : palmier() et feuille(). La fonction palmier permet de créer la base du palmier (le tronc). On remarque le paramètre r, qui sert à préciser l’inclinaison du palmier (0 = droite, 1 = gauche). Cette fonction est principalement composée de goto() qui ne sont pas remplaçables par notre fonction go() car elle ne tracerait pas les bords noirs.

À la fin de cette fonction nous appelons la fonction feuille. Elle crée des demi-cercles créant ainsi des feuilles de palmier avec les for angle in angles, ainsi que des détails à l’intérieur de celles-ci avec les for offset in offsets, qui constituent au final un détail essentiel de notre image.
Nous l’appelons 2 fois dans la fonction palmier, une fois pour le côté gauche et une autre fois pour le côté droit.

def palmier(x, y, col, r) :

    go(x, y)
    color(col)
    pensize(3)
    fillcolor(51, 32, 3)
    begin_fill()

    for i in range(10):

        if r > 0 :
            right(1)
            goto(x, y +  40)
            goto(x + 40, y + 38)
            goto(x + 40, y)
            goto(x, y)
            go(x, y + 40)
            x += 2
            y += 40

        else : 
            left(-7)
            goto(x, y +  40)
            goto(x + 40, y + 38)
            goto(x + 40, y)
            goto(x, y)
            go(x, y + 40)
            x -= 4
            y += 40

    end_fill()
    x, y = pos()
    go(x + 20, y - 1)
    color(0, 0, 0)
    fillcolor(58, 35, 5)
    begin_fill()
    circle(40)
    end_fill()

    go (x+20, y+41)
    feuilles(150,0, 0)
    feuilles(-160,1, 0)
def feuilles(long, dir, i):
    x, y = pos()

    if dir == 1:
        angles = [45, 90, 360]
        offsets_g = [99, -216, 189]
        offsets_d = [-101, -146, -192]
        col = [(11, 97, 1), (10, 77, 2), (5, 59, 0)]

        for angle in angles:
            color(col[i])
            setheading(angle)
            begin_fill()
            circle(long, 110)
            end_fill()
            go(x, y)
            i += 1

        for offset in offsets_g:
            color(6, 44, 2)
            setheading(angle + offset)
            circle(170, 92)
            go(x, y)

        for offset in offsets_d:
            color(6, 44, 2)
            setheading(angle + offset)
            circle(190, -85)
            go(x, y)

    elif dir == 0:
        angles = [135, 180, 90]
        col = [(11, 97, 1), (10, 77, 2), (5, 59, 0)]

        for angle in angles:
            color(col[i])
            setheading(angle)
            begin_fill()
            circle(long, 110)
            end_fill()
            go(x, y)
            i += 1


Et voici les deux palmiers que l’on obtient à la fin de l’exécution du script. Ils sont très légèrement différents pour éviter de créer une répétition :

Le Bateau

Pour représenter le bateau nous avons utilisé 2 fonctions : bateau() et reflets() décrite précédemment, avec des paramètres différents. Le script du bateau est assez peu lisible, mais il marche, et c’est le plus important. Il se constitue principalement de fonctions basiques de turtle afin de tracer les formes du bateau.

def bateau(x, y) :

    setheading(0)
    go(x,y)
    color(0,0,0)
    pensize(3)
    begin_fill()
    forward(40)
    left(45)
    forward(20)
    a, b = pos()
    setheading(0)
    go(x,y)
    left(135)
    forward(20)
    go(a,b)
    go(x,y)
    go(a-40,b)
    setheading(90)
    forward(40)
    right(135)
    forward(40)
    go(a-40,b)
    end_fill()
    hideturtle()


On obtient alors un petit bateau au loin comme celui-ci, qui n’est pas très détaillé, mais qui en donne assez bien l’illusion :

Les Oiseaux

Enfin, en touche finale, pour donner un côté vivant à notre image nous avons rajouté des oiseaux en forme de V. Les oiseaux ont un placement aléatoire grâce à la fonction randint() du module random et l’angle des ailes, ainsi que leur couleur (bien que donnant une différence très subtile) sont aléatoire, ce qui permet à l’image au global d’être moins monotone.

def oiseaux (n):

    for i in range(n) :

        x = randint(0, 250)
        y = randint(0,200)
        a = randint(2, 4)
        taille = a * 10
        angle = randint(50, 70)
        c = randint(0, 30)

        go(x,y)
        pensize(a)
        color(c,c,c)
        setheading(135)
        circle(taille, angle)
        go(x,y)
        setheading(45)
        circle(-taille, angle)
        hideturtle()

Ce script nous a donné comme résultat une image similaire à celle-ci, mais qui ne sera jamais vraiment la même, à cause des paramètres aléatoires de cette fonction. Cette dernière fonction conclue alors le principal de notre code !

Difficultés rencontrées

Durant ce projet, nous nous sommes confrontés à de nombreuses difficultés techniques, mais qui nous auront au final permis d’améliorer nos compétences en python.

Par exemple, un des premiers problèmes que nous avons dû résoudre fut celui du dégradé de couleur, qui a mis beaucoup de temps avant de marcher correctement, et de nombreux essais. De même, la gestion de la position aléatoire des oiseaux a été un défi, nécessitant de nombreux essais, car ils avaient tendance à se superposer ou à se cacher derrière le palmier droit. Le placement des reliefs et le détail des feuilles de palmiers ont aussi pris du temps, car ils demandaient des ajustements minutieux pour que l’image gagne en cohérence. Ce fut d’ailleurs aussi le cas avec les détails des feuilles de palmiers.

En résumé, le déboggage du code a pris du temps, mais il était nécéssaire pour parvenir à notre image finale !

Résultat final

C’est à la toute fin du code que nous appelons toutes ces fonctions dans un ordre précis (du plus éloigné au plus près) pour créer l’image finale :

ciel((255, 154, 92), (154, 0, 255), 1280, 1000)
soleil(250, (255, 203, 61))
mer((154, 0, 254), (255, 154, 92), 1280, 168)
reflets(0, -89, 425, (255, 203, 61), 14, 7)
plage(-640, -360)
reliefs(-640,-250, 1)
oiseaux(6)
reliefs(-640,-400, 2)
palmier(-425, -275, (0, 0, 0), 1)
palmier(380, -250, (0, 0, 0), -1)
reliefs(-640, -360, 3)
bateau(125, -96)
reflets(145, -105, 40, (0, 0, 0), 4, 3)


Tout ce code fut par la suite inséré dans le code fourni par nos enseignant et qui permet d’exporter cette image en .ps dans le format .png. Le code python complet (d’environ 390 lignes) se trouve plus bas, téléchargeable en version compressée.

Le script en .py

Voici le script python si vous voulez vous aussi vous amusez à le manipuler, ou simplement regarder sa construction dans son ensemble.

Merci d’avoir lu cet article ! Nous espérons qu’il vous aura été utile !

Avatar de Pierre B.
Avatar de Robin P.
Art

Un château parmi les cerisiers

Dans le cadre du premier projet de NSI de première, nous avons choisi de réaliser une image printanière au crépuscule, représentant un château japonais entouré de cerisiers en fleur. Nous espérons ainsi pouvoir vous faire profiter d’un petit voyage à notre humble échelle ! Cet article explique comment fonctionnent les principales parties du code utilisé pour produire l’image finale.

L’objectif du projet

Le projet a pour objectif de générer une image en python, à partir de l’art génératif. Ainsi, le module turtle en python, mais également l’emploi de nos connaissances de cours, permettent de faire découvrir ce qu’il est possible de créer en ayant un thème libre qui laisse la place à l’imagination – dans la limite des 404 lignes imposées. Notre thème est donc un château d’inspiration japonaise, au coeur de la floraison des cerisiers.

Une progression pas à pas

La réalisation du projet a été progressive : avant même de s’atteler au code, nous sommes passés par une étape dessin papier, couplée à de nombreuses recherches. Nous pouvons donc affirmer que les cerisiers et le château sont le fruit d’une documentation minutieuse !

Ensuite, notre image, assemblée au fur et à mesure, s’est faite après l’ajout de nombreux éléments de décor. Ainsi, si la structure de base peut sembler simpliste, elle est néanmoins complétée méthodiquement afin de former le rendu final.

C’est donc en progressant à tâtons, réglant chaque élément au pixel près, et en relançant immanquablement le code des dizaines de fois que nous en sommes arrivés ici. Les traces les plus visibles de ce cheminement sont la structure elle même du code, majoritairement en fonction, puisqu’il est plus facile de changer les différents paramètres sous cette forme.

L’article qui suit commente et illustre donc étape par étape la formation du code générant le dessin.

Le ciel et la colline

Tout d’abord, nous avons généré le fond de l’image, composé de deux fonctions. Le ciel, succession de cercles de plus en plus grands, est tracé en quatre étapes : les cercles sont d’abord jaune, puis un dégradé mène la couleur vers le orange, progressivement rougeâtre, qui enfin rempli le reste de l’image. La colline est quand à elle tracée grâce à un très grand cercle vert, légèrement dégradé vers le jaune en haut. Les couleurs et la taille changent en utilisant l’incrémentation progressive par la fonction for in range () de l’indice i.

Une fonction placement, prenant des coordonnées en argument, est employée dès le début, mais également tout au long du programme pour positionner la tortue sans laisser trace de son déplacement.

#fonction qui trace le fond et le dégradé elle est compactée autant que possible.
def fond(r,g,b):
    pensize(4)
    nb_r=[150,260,237,300]
    for j in range(len(nb_r)):
        for i in range(nb_r[j]):
            circle(nb_r[0]*(j//(j-0.1))+(nb_r[1]/2)*((j/2)//1)+(nb_r[2]*((j)//3)//1)+(i*((((((j+1)/(j+1))/(j+1))*2+0.5)//1)/2))*((j//2)+1))
            placement((0,0-nb_r[0]*(j//(j-0.1))-(nb_r[1]/2)*((j/2)//1)-(nb_r[2]*((j)//3)//1)-(i*((((((j+1)/(j+1))/(j+1))*2+0.5)//1)/2))*((j//2)+1)))
            color(int((255-int(79*(j//3)))-(i//((j//2)+2-(j//3)))*(j//2)),0+int(((209-130*(j//2)-(i//(j+1))*(j//(j-0.1))))*(1-((j/3)//1))),0)

#fond détaillé
'''
color(255, 209, 0)
pensize(4)
for i in range (150):
    circle(i)
    placement((0,0-i))
for i in range (260):
    circle(150+(i/2))
    placement((0,-150-(i/2)))
    color(255, 209-i//2, 0)
for i in range(237):
    circle(280+i)
    placement((0,-280-i))
    color(255-i//3,79-i//3,0)
for i in range(300):
    circle(517+i)
    placement((0,-517-i))
    color(176-i//2,0,0)
'''		

#fonction qui trace la colline
def colline():
    pensize(5)
    up()
    fillcolor(84,148, 49)
    begin_fill()
    placement((0,-4000))
    circle(1940)
    down()
    end_fill()
    for i in range(60):
        circle(1940+i//2)
        color(84+i,148+i//3,49)
    
#une fonction qui simplifie le déplacement 
def placement (x):
    up()
    goto(x)
    down()

fond(255,209,0)
colline()

Le château

Le château est une fonction faisant appel à deux autres fonctions, respectivement celles qui tracent les toits et les murs. Partant du sommet de la structure, la fonction château permet d’agrandir au fur et à mesure les étages. Elle calcule donc aussi progressivement les coordonnées des différents éléments selon le coefficient entré en paramètre. Les toits, deux segments reliés par des quarts de cercle, et les murs, des rectangles blancs, ne sont ainsi que de simples figures géométriques dont la taille varie.

#fonction qui trace les toits du chateau

def toit(taille,rayon,x,y):
    color(60,60,60)
    placement((x,y))
    fillcolor(60,60,90)
    begin_fill()
    circle(rayon,90)
    setheading(0)
    forward(taille)
    setheading(270)
    circle(rayon,90)
    backward(taille+(rayon*2))
    end_fill()

#fonction qui trace les murs du chateau

def mur(hauteur,longueur,x,y):
    placement((x,y))
    for _ in range(2):
        color("white")
        fillcolor(255,255,255)
        begin_fill()
        forward(longueur)
        left(90)
        forward(hauteur)
        left(90)
        end_fill()

#structure globale du chateau
def chateau(t):
    car_toit=[30*t,20*t,-((30*t)/2+20*t),215]
    car_mur=[20*t,40*t,-(40*t)/2,car_toit[3]-car_toit[1]]
    for i in range(4):
        toit(car_toit[0]+i*16*t,car_toit[1],car_toit[2],car_toit[3])
        mur(car_mur[0]+2*i*t,car_mur[1]+i*15*t,car_mur[2],car_mur[3])
        car_toit[2] = int(-((car_toit[0]+16*(i+1)*t+40*t)/2))
        car_toit[3] = car_mur[3]-20*t
        car_mur[2] = int(-((car_mur[1]+15*(i+1)*t)/2))
        car_mur[3] = car_toit[3]-(car_mur[0]+2*(i+1)*t)

chateau(2)

Les finitions du château

Dans une volonté d’imiter les véritables château japonais, nous avons décidé de rajouter des éléments permettant une forme d’authenticité. Nous avons donc choisi de représenter les toits de face par des triangles isocèles de différentes tailles. Les fenêtres sont, quand à elles, de simples rectangles dont l’espacement et le nombre sont définis dans les arguments. De même, la porte principale est composée de deux rectangles marrons dont les contours sont gris. Une utilisation de la fonction toit (voir la section le château), qui couvre l’entrée, achève de compléter le château.

#ornements du chateau 
def toit_face(x,taille):
    color(60,60,60)
    placement(x)
    fillcolor(60,60,90)
    setheading(0)
    begin_fill()
    forward(taille)
    left(150)
    forward(taille*0.6)
    left(60)
    forward(taille*0.6)
    end_fill()

#fonction qui rajoute les fenetres du chateau
def fenetres(x,y,espacement,nb_fenetre):
    color(60,60,60)
    placement((x,y))
    for n in range(nb_fenetre):
        setheading(0)
        begin_fill()
        for i in range(2):
            forward(4)
            left(90)
            forward(7)
            left(90)
        end_fill()
        placement((x+(espacement*(i+n)),y))

#fonction qui trace la porte du chateau
def porte(longueur,hauteur,x,y):
    pensize(4)
    color(60,60,60)
    placement((x,y))
    for i in range(2):
        for i in range(2):
            fillcolor(71, 27, 12)
            begin_fill()
            forward(longueur)
            left(90)
            forward(hauteur)
            left(90)
            end_fill()
        placement((x+longueur,y))
        
toit_face((-45,91),45)
toit_face((0 ,91),45)
toit_face((-30,3),60)
fenetres(-27,195,25,3)
fenetres(-60,27,30,2)
fenetres(28,27,30,2)
porte(20,30,-20,-89)
toit(36,12,-30,-59)

Les cerisiers

La fonction pour tracer un cerisier fait appel à deux autres fonctions. La première trace le tronc, tandis que la seconde complète l’arbre en y ajoutant les branches. Ces dernières sont toutes différentes : les arguments entrés permettent notamment de régler la taille, et la présence éventuelle d’une fleur. Des listes sont également employées afin d’enregistrer les positions au fur et à mesure, facilitant grandement le code. Enfin, les pétales tombés au sol sont placés aléatoirement, à l’aide du module random. Chaque dessin tracé grâce à ce code est ainsi unique !

positions=[]
pos2=[]

#fonction qui trace le tronc d'un cerisier
def tronc(t):
    positions.append(pos())
    begin_fill()
    setheading(70)
    forward(25*t)
    left(20)
    forward(75*t)
    right(90)
    positions.append(pos())
    forward(20*t)
    positions.append(pos())
    right(90)
    forward(70*t)
    left(25)
    forward(30*t)
    placement(positions[0])
    end_fill()
    placement(positions[2])

#fonction qui trace les branches du cerisier
def branche(n,r,s,t):
    pos_fill=pos()
    begin_fill()
    setheading(r)
    forward((50/n*t)/2)
    if n==1:
        pos2.append(pos())
    forward((50/n*t)/2)
    right(90*s)
    if n<3:
        positions.append(pos())
    forward(8/n*t)
    if n<3:
        positions.append(pos())
    right(90*s)
    forward((70/n*t)/2)
    if n==1:
        pos2.append(pos())
    forward((70/n*t)/2)
    placement(pos_fill)
    end_fill()
    if n>=3:
        backward(70/n*t)
        color(244, 194, 194)
        pensize(10)
        forward(1)
        pensize(1)
        color(90,22,6)

#fonction qui trace un cerisier
def cerisier(x,t):
    ori_br=[35,77,102,125,150]
    fleur(x)
    color(90,22,6)
    fillcolor(90,22,6)
    placement(x)
    tronc(t)
    direc=1
    for i in range(5):
        branche(1,ori_br[i],direc,t)
        placement(pos2[-2])
        branche(3,ori_br[i]+35,1,t)
        placement(pos2[-1])
        branche(3,ori_br[i]-35,-1,t)
        placement(positions[2])
        if i<2:
            placement((pos()[0]-(i+1)*8,pos()[1]))
        else:
            direc=-1
            placement((pos()[0]-16,pos()[1]))
    for j in range(2):
        for i in range(5+j*5):
            for k in range(2-j):
                placement(positions[(i*2)+(j*10)+3])
                branche(2+j+k,ori_br[i//(1+j)]+30+(j*30)+(k*40),-1,t)
                placement(positions[(i*2)+(j*10)+4])
                branche(2+j+k,ori_br[i//(1+j)]-20+(j*-20)+(k*-30),1,t)
            placement(((positions[(i*2)+(j*10)+3][0]+positions[(i*2)+(j*10)+4][0])/2,(positions[(i*2)+(j*10)+3][1]+positions[(i*2)+(j*10)+4][1])/2))
            branche(3,ori_br[i//(1+j)],1,t)
    positions.clear()
    pos2.clear()

#fonction qui trace les fleurs de cerisier sur le sol
def fleur(pos):
    for i in range(10):
        placement((pos[0]+randint(-45,50),pos[1]+randint(-40,30)))
        color('#FFC0CB')
        pensize(10)
        forward(1)
        pensize(1)
        
cerisier((-190,-150),0.75)
cerisier((-310,-300),0.75)
cerisier((-490,-240),0.75)
cerisier((150,-280),0.75)
cerisier((280,-160),0.75)
cerisier((460,-220),0.75)

Une touche de réalisme

Enfin, le dessin se complète avec de légers détails dans le décor, comme les buissons et le chemin au sol, mais également les oiseaux dans le ciel. Si le chemin est un trapèze beige, les buissons sont tracés par une fonction combinant différents morceaux de cercles. Les oiseaux sont, quant à eux deux segments reliés rendu dissemblables par leur taille et leur angle variables. De même, les nuages, assemblages de grands cercles, sont de différentes tailles et formes selon les variables entrées lors de l’appel de la fonction nuage.

#fonction qui trace le petit chemin
def chemin():
    color(0,0,0)
    fillcolor(210, 180, 140)
    placement((-45,-360))
    begin_fill()
    placement((-20,-89))
    placement((20,-89))
    placement((45,-360))
    placement((-45,-360))
    end_fill()

#fonction qui trace les nuages
def nuage(taille,x,y,deformation):
    color(148, 0, 0)
    pensize(taille)
    placement((x,y))
    setheading(deformation)
    for i in range (4):
        penup()
        forward(1)
        forward(taille/2)
        pendown()
        forward(1)
        right(90)
    placement((x-(taille//2),y-(taille//3)))
    forward(1)
    placement((x+(taille),y-(taille//3)))
    forward(1)


#fonction qui trace les buisson
def buisson(coordonne):
    pensize(1)
    placement(coordonne)
    setheading(270)
    fillcolor(46,102,42)
    begin_fill()
    circle(40,-60)
    circle(10,-80)
    left(110)
    circle(30,-120)
    left(110)
    circle(20,-90)
    circle(60,-40)
    circle(9,-85)
    circle(210,-34)
    circle(7,-85)
    end_fill()

#fonction qui trace les oiseaux
def oiseau(taille,battement,x,y):
    color('black')
    pensize(2)
    placement((x,y))
    setheading(-battement)
    forward(taille)
    left(battement*2)
    forward(taille)
    
chemin()
buisson((-210,-285))
buisson((-380,-150))
buisson((225,-290))
buisson((490,-300))
nuage(50,200,270,12)
nuage(60,400,100,23)
nuage(35,550,300,-12)
oiseau(15,20,-270,90)
oiseau(10,30,-190,145)
oiseau(10,25,-170,110)
oiseau(12,35,-230,160)

Les difficultés

Cela peut sembler surprenant, mais notre premier et principal obstacle était la difficulté à trouver un thème, et par extension une difficulté à concevoir le rendu global de l’image. Méthodiquement, nous avons donc couché sur du papier toutes les idées qui nous venaient, puis nous avons fini par opter pour le château.

De plus, nous avons été confronté à l’opposition entre le dessin, qui se voulait réaliste, et ce que nos connaissances en python – et en particulier du module turtle – nous permettaient de réaliser, autrement dit des figures géométriques essentiellement. Nous avons ainsi trouvé un compromis, en simplifiant le château et le décor, mais avons néanmoins cherché un semblant de réalité avec les détails des cerisiers, leurs pétales tombés aléatoirement au sol, et les nuages dont aucun n’est identique à un autre.

Télécharger le script complet :

Art

Extraction minière au sein de l’Himalaya

Le premier projet de la classe de 1ère NSI est « Image générative ». Ce projet consiste en la création d’une image sur thème libre. Pour notre image, on a choisi d’aborder les thèmes de la montagne, du ciel et de la mine. Nous avons voulu représenter ainsi, une mine présente au sein de la chaine de montagnes de l’Himalaya sous un ciel étoilé de nuit avec une lune en fond venant se glisser derrière la montagne.

Le fond

Afin de réaliser le fond de notre image, nous avons voulu réaliser un fond dégradé du bleu foncé vers le bleu clair afin de représenter la nuit tombante.

def fond():
    for i in range(720):
        r = 173 - round((i / (720 / 173)))  
        v = 216 - round((i / (720 / 216))) 
        b = 230 - round((i / (720 / 102)))
        pencolor(r, v, b)
        forward(1280)
        goto(-640, -360 + i)

fond()

Les étoiles

Ensuite pour réaliser l’ambiance de nuit tombante, nous avons fait le choix de rajouter des étoiles. Pour cela nous avons utilisé le module « randint » nous permettant de placer les étoiles de manière aléatoire.

def etoile():
    pencolor("#fff7cd")
    fillcolor("#fff7cd")
    for _ in range(130):
        penup()
        x = randint(-650, 650)
        y = randint(-360, 360)
        goto(x, y)
        pendown()
        begin_fill()
        circle(randint(1, 4))
        end_fill()
etoile()

Lune

Pour rester dans cette ambiance de nuit tombante nous avons rajouté une lune en fond à laquelle nous avons rajouté des taches pour que celle ci soit plus fidèle à la vrai lune.

def lune():
    penup()
    goto(10,-50)
    color("#EEF0F1")
    fillcolor("#EEF0F1")
    begin_fill()
    circle(220)
    end_fill()
lune()

def taches(x, y,z):
    for i in range(15):
        penup()
        goto(x, y)
        pendown()
        shapesize(z)
        pencolor("#000000")  
        fillcolor("#a4a4a4")  
        begin_fill()  
        circle(z)
        end_fill()
taches(100, 200,20)
taches(-110, 100,50)
taches(-90, 250,30)
taches(0, 200,15)
taches(70, 75,42)
taches(70, 270,24)
taches(150, 100,12)

Les montagnes

Ici nous avons créer des montagnes sous forme de chaine où logera par la suite notre grotte minière. Nous avons rajouter un rectangle afin de combler le trou entre les montagnes même si cela est peux conventionnel.

def triangle(x,y,z):
    penup()
    goto(x,y)
    pendown()
    pensize(4)
    color("#757575") 
    fillcolor("#757575")
    begin_fill()
    for i in range(3):
        forward(500)
        left(120)    
    end_fill()
triangle(-235,-200,10)
triangle(70,-350,10)
triangle(-600,-350,10)
triangle(-450,-400,10)
triangle(-800,-500,10)
triangle(310,-500,10)
def rectangle():
    penup()
    goto(-110,-250)
    pendown()
    pensize(150)
    pencolor("#757575")
    forward(400) #Peu conventionnel
rectangle()

Ombre des montagnes

Afin de rajouter du réalisme à notre image nous avons décidé de rajouter une ombre derrière les montagnes en suivant presque le même schéma que celles ci.

def ombre(x,y,z):
    penup()
    goto(x,y)
    pendown()
    pensize(4)
    color("#323232") 
    fillcolor("#323232")
    begin_fill()
    for i in range(3):
        forward(500)
        left(120)    
    end_fill()
ombre(-280,-200,10)
ombre(30,-350,10)
ombre(-640,-350,10)
ombre(-490,-400,10)
ombre(-840,-500,10)
ombre(270,-500,10)

Le sol

Nous avons décidé de faire un sol dégradé marron foncé afin de rappeler la couleur de la terre et aussi se rapprocher du modèle réel présent dans la réalité.

def sol():
    for i in range(107):
        r = round(101 + (30 - 60) * (i / 107))  
        v = round(67 + (1 - 20) * (i / 107))    
        b = round(33 + (1 - 20) * (i / 107))    
        pencolor(r, v, b)                     
        forward(1280)                        
        goto(-640, -360 + i)                
sol()

La grotte

La grotte était notre idée principale ainsi nous avons décidé de la mettre au centre. Pour marquer la profondeur de celle ci nous avons choisi le noir pour récréer l’obscurité de la grotte.

def entree(x,y,z):
    penup()
    goto(x,y)
    pendown()
    pensize(4)
    color("#000000") 
    fillcolor("#000000")
    begin_fill()
    for i in range(3):
        forward(z)
        left(120)    
    end_fill()

entree(-120,-253,150)
entree(-90,-253,165)
entree(40,-253,50)

La neige

Toujours dans la dynamique de ressemblance à la réalité, nous avons fait le choix de rajouter au sommet des montagnes de la neige comme sur les sommet de l’Himalaya. Pour cela nous avons encore utilisé la fonction triangle que nous avons préalablement définie.

def neige(x,y,z,w):
    penup()
    goto(x,y)
    pendown()
    pensize(4)
    color("#f7f7f7") 
    fillcolor("#f7f7f7")
    begin_fill()
    for i in range(3):
        forward(z)
        left(w)    
    end_fill()
neige(-49,120,130,120)
neige(-50,120,30,-120)
neige(-20,120,50,-120)
neige(35,120,15,-120)
neige(50,120,30,-120)

neige(273,0,95,120)
neige(273,0,30,-120)
neige(300,0,15,-120)
neige(310,10,40,-120)
neige(350,0,15,-120)

neige(-397,0,95,120)
neige(-397,0,30,-120)
neige(-340,0,15,-120)
neige(-365,5,25,-120)
neige(-328,0,25,-120)

neige(-225,-10,50,120)
neige(-225,-10,10,-120)
neige(-210,-10,20,-120)
neige(-190,-10,15,-120)

neige(-575,-110,50,120)
neige(-575,-110,10,-120)
neige(-560,-110,15,-120)
neige(-548,-107,10,-120)
neige(-540,-110,15,-120)

neige(535,-110,50,120)
neige(535,-110,18,-120)
neige(555,-110,9,-120)
neige(565,-110,20,-120)

Relief

Toujours dans une démarche de réalisme, nous avons voulu apporter un relief à, la montagne paraissant plate. Pour cela nous avons simplement tracé des traits prolongeant certaines arrête des montagnes.

def delimitation(x,y,z,w):
    pencolor("#2c2c2c")
    pensize(3)
    penup()
    goto(x,y)
    pendown()
    goto(z,w)
delimitation(-60,110,-208,-150)
penup()
delimitation(-402,0,-519,-210)
penup()
delimitation(270,-1,145,-210)

Chariot

En accord avec notre première idée, nous avons rajouté un chariot à la mine pour que de un l’image soit plus compréhensible mais aussi pour venir rajouter des détail à notre image. Nous avons fait le choix également de rajouter des rails pour celui ci.

Problèmes rencontrés

Nous avons eu de nombreux problèmes durant la création de notre image. En effet lors de faire la lune nous avions eu pour idée de rendre les taches aléatoire avec le module « randint » , mais cela n’allais pas car les taches se superposais et ne changeais pas de taille. Aussi lorsque nous avons fais le wagon nous réfléchissions a un moyen de le remplir réalistement mais nous n’avons pas trouver alors nous avons fais le choix de placer de simples triangles.

Sources

Afin de réaliser notre image, nous avons utilisé plusieurs sources : Tout d’abord nous avons utilisé chat gpt afin de déboguer certaines parties de notre code, ensuite nous avons utilisé le forum « stack overflow » pour trouver notre fond et réaliser notre dégradé, enfin nous avons utilisé le site « python guides » pour nous renseigner sur des commandes notamment sur l’utilisation du module « random » lors de la création des étoiles.

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

Art

La culture Espagnole

Nous avons choisi le thème de l’Espagne car c’est une une de mes origines, c’est donc un thème qui me tient à cœur. À travers ce titre et ce thème, nous voulons montrer un petit bout de la culture espagnole, c’est pour cela que nous avons choisi la Porte d’Alcala située à Madrid et Alhambra située à Grenade.

Les 2 monuments

La porte d’Alcala est située à Madrid et l’une des 5 anciennes portes qui donnaient accès à la ville. Elle a été construite entre 1769-1778.

L’Alhambra est un ensemble fortifié situé à Grenade, en Andalousie. Cet ensemble a été construit par les Arabes, puis agrandit par la suite par Charles Quint

Le Fond

Nous avons d’abord décidé de créer un fond bleu ciel, puis l’image est coupée en deux par la fonction ligne ci-dessous :

def ligne(ax,ay,bx,by,color_choice) :
color(color_choice)
penup()
goto(ax,ay)
pendown()
goto(bx,by)
penup()

Nous choisissons donc des coordonnées de départ et d’arrivée. Le choix de mettre « color_choice » sera vu plus tard dans l’article.

Ensuite, la création d’une fonction herbe a été nécessaire, puis pour les nuages nous avons donc créé la fonction nuage ci-dessous :

def nuage(x, y):
    penup()
    goto(x,y)
    pendown
    color("white")
    for i in range(3):
        begin_fill()
        circle(20)
        end_fill()
        penup()
        forward(30)
        pendown()

La boucle permet la création de 3 cercles côte à côte, ce qui nous permettra de faire le nuage. Pour des raisons d’esthétiques… nous préférons choisir leur emplacement manuellement qu’à l’aide du module ramdom. Ce la nous donne le résultat suivant :

Les fonctions principales

Outres la fonction ligne deux autres fonctions sont importantes :

  • La fonction colonnes nous permets de créer les colonnes de la taille et de la largeur que l’on veut dans les deux monuments :
def colonne(x, y, hauteur, largeur):
    penup()  
    goto(x, y)  
    pendown()  
    color("#9E9FA5")  
    begin_fill()
    for _ in range(2):
        forward(largeur)  
        right(90)  
        forward(hauteur)  
        right(90)  
    end_fill()

La boucle permet la création d’un rectangle qui représente les colonnes

  • Nous avons ensuite créé une fonction arche qui va aussi servir dans les deux monuments. Une fonction plutôt simple à faire…
def arche(x,y,r):
    pensize(15)
    penup()
    goto(x, y)
    pendown()
    color('#9E9FA5')
    setheading(90)
    circle(r,180)
    setheading(90)

Et évidemment tout est saisi manuellement après quelques calculs, bien entendu. Toutes ces fonctions nous perment de créer les monuments.

Fonction Madrid

La fonction Madrid nous crée le premier monument qui est la porte d’Acala, nous n’allons pas nous attarder sur toute la fonction car elle est très longue, nous allons nous attarder sur l’important.

La 1ère phase a été de créer un bâtiment gris comme celui qui suit :

Voici comment nous avons eu le résultat :

  • D’abord, nous avons utilisé la fonction herbe montait et grâce à une indentation, l’herbe montait de plus en plus, jusqu’à ce que la boucle soie finie
  • Ensuite la fonction colonne rentre en jeu, mais il y a une petite particularité
    colonne(70, -10, 254, 29)
    penup()
    pendown()
    colonne(150, -75, 190, 29)
    penup()
    pendown()
    colonne(250, -75, 190, 29)
    penup()
    pendown()
    colonne(350, -75, 190, 29)
    penup()
    pendown()
    colonne(450, -75, 190, 29)

La particularité, comme vous devez le voir, c’est que la 1ère colonne et la dernière colonne sont plus grandes que les 3 milieux. Ce choix s’explique par le fait que la définition ligne finit par des côtés ronds, les colonnes à l’extrémité donnent donc une forme plus géométrique à notre monument.

  • Ensuite, la fonction arche est utilisée pour les 3 portes du milieu. En effet, celles sur le côté sont plus petites et rectangulaires, c’est pour cela que la fonction ligne va être réutilisée.
  • Pour finir la Première phase et commencer la deuxième, nous avons utilisé la fonctions ligne, et c’est là qu’intervient le choix de mettre « color_choice » cela à pour but faire un petit contraste pour refaire un petit peu les détails du monument.
ya = -79
    yb = -79
    pensize(13)
    for i in range(10) :
        ligne(97, ya, 152, yb, "#9E9FA5")
        ya -= 7
        yb -= 7
    for i in range(5) :
        ligne(256, ya,344, yb, "#B4B4B8")
        ya += 5
        yb += 5
    pensize(5)

Tout d’abord, comme nous le voyons, la couleur va soit être dans un 1er cas gris foncé ou soit gros clair. Ensuite, pour faire les bonnes dimensions on adaptait toujours les coordonnées, les indentations soit += soit -= et la répétition et la taille du stylo.

  • Enfin, n’ayant pas trouvé comment un triangle, nous avons utilisé Thalès et Pythagore, puis la commande goto pour faire le triangle tout en haut.

La fonction Madrid nous donnes donc le résultat suivant :

Avec 1 couleur :

Avec 2 couleurs :

La fonction étant longue, vous pourrez la retrouver dans le document ci-joint.

Fonction Grenade

Après avoir fait la fonction Madrid, on a crée grâce à la fonction Grenade un bâtiment de l’Alhambra. Comme avant nous allons nous attarder sur le plus important.

La 1ère phase a été de créer le contours du bâtiment de couleur tan comme celui ci-dessous :

Pour ça, nous avons d’abord avons utilisé :

  • la fonction colonne et la fonction arche qui nous aide à faire la base du bâtiment. L’utilisation des fonction est pareil que dans la fonction Madrid.
    color("#D2B48C")
    colonne(-105, -285, 20, 165)
    penup()
    pendown()
    colonne(-195, -285, 20, 165)
    penup()
    pendown()
    colonne(-290, -285, 20, 165)
    penup()
    pendown()
    colonne(-387, -285, 20, 165)
    penup()
    pendown()
    colonne(-480, -285, 20, 165)
    penup()
    pendown()
    colonne(-570, -285, 20, 165)
    arche(-100,-154,42, "#D2B48C")
    arche(-190,-154,42, "#D2B48C")
    arche(-287,-154,42, "#D2B48C")
    arche(-383,-154,42, "#D2B48C")

Ici vous voyez qu’entre les deux fonctions, celle de Madrid et celle Grenade on a rajouté color_choice comme pour la fonction lignes car on avait besoin de changer de couleur entre les deux bâtiments.

  • Ensuite, c’est le grand retour de la fonction ligne, et celle là, a encore beaucoup servi. Car pour la première phase elle à servir à faire le début du contours du bâtiment. Son utilisation est toujours la même :
ligne(-565,-145,-565,44,"#D2B48C")
  • Dans la deuxième phase, on a fini les contours du bâtiment et on à remplit tout avec des indentations. Cela nous donne donc ce rendu :
  • Ici on a encore utilisé beaucoup d’indentation et de boules, notamment de indentation += ou +-:
for i in range(13):
        ligne(-415, ya, -235, yb, "#D2B48C")
        ya += 7
        yb += 7
for i in range(2):
        if i==1:
            ya=44
            yb=44
            for i in range(4) :
                ligne(-565, ya, -481, yb, "#E3D9B6")#Arrivé e départ a améliorer
                ya -= 7
                yb -= 7
        else :
            ya=44
            yb=44
            for i in range(4) :
                ligne(-90, ya, -174, yb, "#E3D9B6")#Arrivé e départ a améliorer
                ya -= 7
                yb -= 7
  • Enfin on utilise une nouvelle fonction qui s’appelle la fonction rempart, cette fonction nous permet de créer les remparts du monument. Son script est le suivant, elle est répété 5 fois dans a fonction Grenade.
def remparts(n) :
    right(90)
    i = 0
    for i in range(n):
        left(90)
        forward(20)
        right(90)
        forward(20)
        right(90)
        forward(20)
        left(90)
        i +=1
        if i < 5 :
            forward(20)

Cette fonction nous donnes le rendu final qui ressemble à ça :

Conclusion

Pour finir, j’espère que cette article vous on aura appris plus sur le codage python. Le fichier est joint à l’article si vous voulez le voir en detail.

N’hésitez pas à aller visiter l’Espagne et notamment c’est deux monument, ils sont plus jolis dans la vrai qu’en code python…

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.

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é

Art

De l’art sur la NumWorks

Les calculatrices graphiques programmables sont bien plus que de simples outils pour les calculs. Elles ouvrent un monde de possibilités aux élèves, en permettant d’explorer les mathématiques d’une manière interactive et visuelle. Mais leur potentiel va au-delà des formules et des graphiques : elles peuvent être utilisées pour créer de l’art numérique, des animations, et même des jeux !

En programmant ces machines, les étudiants développent leur créativité tout en renforçant leurs compétences en logique et en résolution de problèmes. Ces calculatrices deviennent ainsi des alliées indispensables pour apprendre de manière ludique et innovante, et pour repousser les limites de l’imagination.

Depuis 2019, j’invite mes élèves à explorer leur potentiel créatif en réalisant des dessins uniques, soit avec l’application Grapheur, soit avec l’application Python de la calculatrice. Chaque année, leurs œuvres sont soumises à l’éditeur de la calculatrice, NumWorks, qui sélectionne la plus belle réalisation. L’auteur de cette création se voit offrir une coque au design original pour sa calculatrice. Si vous croisez un élève au lycée Louis Pasteur arborant une telle coque, il est fort probable qu’il soit lauréat de l’un de ces concours.

Certaines de ces réalisations sont également partagées sur les réseaux sociaux, notamment sur Instagram et X (anciennement Twitter). Une quinzaine de ces images ont même été sélectionnées et publiées, avec l’accord de leurs auteurs, dans un livre intitulé Découvrir la calculatrice graphique NumWorks édité en Français et en Anglais.

Il est vrai que se lancer dans la programmation sur une calculatrice graphique peut sembler déroutant au premier abord. Le défi de traduire des idées créatives en code peut déstabiliser, surtout pour ceux qui n’ont jamais exploré cet univers. Cependant, une fois les premières hésitations surmontées, les élèves découvrent rapidement le plaisir de voir leurs concepts prendre vie à l’écran. Ce processus, bien qu’exigeant, devient une source de satisfaction personnelle, les encourageant à se dépasser. En fin de compte, ils s’amusent tout en repoussant leurs propres limites, transformant un défi intimidant en une expérience enrichissante.

Tout à commencé en 2019 …

En 2019, après réflexion et concertation de l’équipe des enseignants de mathématiques, nous avons pris la décision collective de passer aux calculatrices NumWorks dans toutes nos classes. Pour familiariser les élèves avec cet outil, leur premier travail a été de réaliser un projet simple, destiné à leur faire découvrir les différentes fonctionnalités de la calculatrice. Sans attente particulière, j’ai été agréablement surpris par l’enthousiasme et la créativité dont ils ont fait preuve. Certaines productions, bien au-delà de ce que j’avais imaginé, ont révélé un potentiel insoupçonné chez les élèves, ouvrant la voie à des projets encore plus ambitieux par la suite.

Le titre du sujet était : Les mathématiques sont belles. Voici les 30 réalisations des élèves.

Certaines de ces images sont issues de scripts bien connus, adaptés et optimisés pour la NumWorks. Elles ont été réalisées à distance, en ligne, pendant le confinement de mars 2020. J’ai alors demandé à mes élèves de Terminale de constituer un jury pour évaluer ces créations, en insistant sur l’importance d’une bienveillance maximale dans leurs délibérations. Bien que les codes Python aient parfois été maladroits, ces erreurs faisaient partie intégrante du processus d’apprentissage. Se tromper, tâtonner, recommencer et s’améliorer sont autant d’étapes essentielles dans le développement de leurs compétences.

Quelques thread colorés publiés sur X (Twitter)

J’ai rejoint X (anciennement Twitter) durant le confinement, à l’origine pour passer le temps. Depuis, j’ai régulièrement partagé des threads mettant en avant les créations de mes élèves. En voici quelques exemples. (Un tableau à la fin de cet article propose davantage de liens.)

https://twitter.com/nsi_xyz/status/1380526907477884930
https://twitter.com/nsi_xyz/status/1578772799136890880
https://twitter.com/nsi_xyz/status/1593645740219416577
https://twitter.com/nsi_xyz/status/1770189028366709193
https://twitter.com/nsi_xyz/status/1714607831733707112
https://twitter.com/nsi_xyz/status/1802272199455187027

De l’art sur la NumWorks depuis 2020

Le tableau ci-dessous récapitule tous les fils de discussion publiés sur X (anciennement Twitter). Malheureusement, depuis le rachat de Twitter par Elon Musk, il n’est plus possible de consulter ces fils sans posséder un compte sur la plateforme. Cette restriction illustre bien les défis liés à l’utilisation d’outils fermés, qui peuvent rapidement se transformer en véritables obstacles à l’accès à l’information. 🥴

A compter novembre 2024, compte tenu de la fermeture progressive de X, de l’abus des publicités et de la toxicité du réseau, les fils seront publiés sur Blue Sky.
Les fils sur X existent toujours, en cliquant sur le T vous pouvez le lire sans compte sur X (Twitter)

Création
(1er éd.)
Titre du travail à rendre
Classe cible
Thread X (Twitter)
Blue Sky
2019
(2020)
Les mathématiques sont belles.
Seconde, mathématiques
2020 2021 2022 2023 2024 2025
2021
(2021)
Le Python et la Tortue.
1ère, spécialité NSI
2021 2022 2023 20242025
2021
(2021)
Le python et les fractales
Tale, spécialité NSI
2021 2022 2023 20242025
2022
(2022)
Les mathématiques sont belles ! ed. spéciale cercle
Tale, option Mathématiques Expertes
2022 2023 2024
2022
(2023)
Les mathématiques sont belles ! ed. spéciale polynôme
Tale, option Mathématiques Expertes
2023, 2024 20252025
2022
(2024)
Un feu d’artifice en python
Tale, option Mathématiques Expertes
2024 20252025
2023
(2023)
Le Python et la Tortue.
Seconde, SNT
2023 20242025
2024
(2024)
Pixel Art en python
Tale, option Mathématiques Expertes
20242025

Art

La Citrouille et la Tortue

Pour notre premier projet en classe de 1ère NSI, nous nous sommes basées sur le thème d’halloween.
Ici vous trouverez nos démarches pour créer cette image de style pop. Notre image est composée de quatre parties comportant chacune un fond et une citrouille de couleur différente.

Vidéo de présentation du projet :

Le projet :

Ce projet consiste à créer de l’art génératif grâce à un script python qui doit principalement utiliser des fonctions. Pour le réaliser, nous avons utilisé le module turtle pour tracer l’image et la fonction randint du module random pour pouvoir transformer notre image (.py) en png. Le thème de l’image générée était libre, elle pouvait même être abstraite !

Le processus de création :

Durant ce projet, nous sommes passées par plusieurs étapes pour concevoir l’image et répondre aux attendus.

Nous avons tout d’abord dû trouver une idée de l’image que l’on voulait créer. Pour cela, nous nous sommes inspirées de l’artiste américain Andy Warhol et de ses oeuvres comme « Shot Marilyns » et nous avons transposé son style dans le thème d’Halloween.

Après avoir dessiné un croquis à la main, nous avons créé le script d’une citrouille puis nous l’avons transformé en plusieurs fonctions pour pouvoir la reproduire en quatre exemplaires. Nous avons fait de même pour les rectangles.

Contrairement aux citrouilles et aux rectangles, nous avons tout de suite codé les lettres en tant que fonctions ce qui nous a permis d’aller plus vite dans la conception de l’image.

Enfin, nous avons dû organiser notre script rigoureusement pour que la tortue trace les éléments dans le bon ordre pour un rendu propre et travaillé.

Le code expliqué étape par étape :

Pour concevoir cette image, nous avons agi étape par étape. Nous avons donc relevé les différentes parties du code qui ont permis de tracer l’image. (Nous ne montrerons pas la mise en place des modules et comment nous avons généré des images automatiquement. Pour cela, vous pouvez vous rendre sur cet article.)

Le fond :

Tout d’abord, nous avons divisé l’écran en quatre parties égales et rectangulaires. Nous avons défini une fonction rectangle(x,y,color). Pour obtenir nos quatre parties distinctes, il suffit de mettre les coordonnées ainsi qu’une couleur en argument pour tracer les rectangles au bon endroit.

# trace les rectangles
def rectangle(x, y, color):
  up()
  goto(x, y)
  down()
  pencolor(color)
  fillcolor(color)
  begin_fill()
  for i in range(2):
    forward(640)
    right(90)
    forward(360)
    right(90)
  end_fill()

Nous appelons donc la fonction avec des coordonnées et des couleurs différentes pour tracer nos quatre rectangles.

rectangle(-640, 360, "#A8608E")
rectangle(-640, 0, "#25AE80")
rectangle(0, 360, "#C9BB32")
rectangle(0, 0, "#E1770C")

Résultat :

L’écriture « HAPPY HALLOWEEN » :

Ensuite, nous avons créé des fonctions pour chaque lettre différente composant l’expression « HAPPY HALLOWEEN » soit une fonction pour les lettres h, a, p, y, l, o, w, e et n. La lettre « o » est spéciale car nous avons voulu la représenter par une citrouille pour rester dans le thème d’Halloween. Ici, nous vous montrerons les fonctions lettre_h(x,y), lettre_o(x,y) et lettre_e(x,y).

# trace la lettre h en majuscule
def lettre_h(x,y):
  pensize(3)
  color("black")
  up()
  goto(x,y)
  setheading(90)
  down()
  forward(50)
  backward(25)
  right(90)
  forward(25)
  right(90)
  forward(25)
  backward(50)


# la lettre o est représentée par une citrouille
def lettre_o(x,y,f):
  pensize(3)
  color("black")
  fillcolor(f)
  up()
  goto(x,y+25)

  a = x
  b = y
  for i in range(2):
    up()
    goto(x, y)
    down()
    begin_fill()
    circle(25)
    end_fill()
    x += 25 / (35 / 15)
    y -= 25 / 14

  a = x + 25 / 1.4
  for i in range(2):
    up()
    goto(a, b)
    down()
    begin_fill()
    circle(25)
    end_fill()
    a -= 25 / (35 / 15)
    b -= 25 / 14

  goto(a + 25 / 14, b)
  begin_fill()
  circle(25)
  end_fill()

  setheading(0)
  up()
  goto(a, b + 25 * 2 + 2)
  down()
  pencolor("black")
  left(350)
  for i in range(4):
    left(40)
    forward(25 / (35 / 15))
    goto(a, b + 25 * 2 + 2)


# trace la lettre e en majuscule
def lettre_e(x,y):
  pensize(3)
  color("black")
  up()
  goto(x,y)
  setheading(90)
  down()
  forward(50)
  right(90)
  forward(25)
  backward(25)
  left(90)
  backward(25)
  right(90)
  forward(15)
  backward(15)
  left(90)
  backward(25)
  right(90)
  forward(25)

Après avoir créé toutes ces fonctions, nous les appelons dans un ordre précis pour écrire « HAPPY HALLOWEEN » et nous utilisons des boucles for pour que l’expression soit répétée et forme un motif. Nous rajoutons également la fonction stamp() qui permet de laisser l’empreinte de la tortue à la fin de la lettre « N ».

h = 20
for i in range(4):
  lettre_h(50,h)
  lettre_a(80,h)
  lettre_p(110,h)
  lettre_p(140,h)
  lettre_y(170,h)
  lettre_h(230,h)
  lettre_a(260,h)
  lettre_l(290,h)
  lettre_l(320,h)
  lettre_o(370,h,"#C9BB32")
  lettre_w(460,h)
  lettre_e(510,h)
  lettre_e(540,h)
  lettre_n(570,h)
  stamp()
  h += 90


h = -340
for i in range(4):
  lettre_h(50,h)
  lettre_a(80,h)
  lettre_p(110,h)
  lettre_p(140,h)
  lettre_y(170,h)
  lettre_h(230,h)
  lettre_a(260,h)
  lettre_l(290,h)
  lettre_l(320,h)
  lettre_o(370,h,"#E1770C")
  lettre_w(460,h)
  lettre_e(510,h)
  lettre_e(540,h)
  lettre_n(570,h)
  stamp()
  h += 90


h = 20
for i in range(4):
  lettre_h(-600,h)
  lettre_a(-570,h)
  lettre_p(-540,h)
  lettre_p(-510,h)
  lettre_y(-480,h)
  lettre_h(-420,h)
  lettre_a(-390,h)
  lettre_l(-360,h)
  lettre_l(-330,h)
  lettre_o(-280,h,"#A8608E")
  lettre_w(-190,h)
  lettre_e(-140,h)
  lettre_e(-110,h)
  lettre_n(-80,h)
  stamp()
  h += 90


h = -340
for i in range(4):
  lettre_h(-600,h)
  lettre_a(-570,h)
  lettre_p(-540,h)
  lettre_p(-510,h)
  lettre_y(-480,h)
  lettre_h(-420,h)
  lettre_a(-390,h)
  lettre_l(-360,h)
  lettre_l(-330,h)
  lettre_o(-280,h,"#25AE80")
  lettre_w(-190,h)
  lettre_e(-140,h)
  lettre_e(-110,h)
  lettre_n(-80,h)
  stamp()
  h += 90
shape("turtle")
stamp()

Résultat :

Le corps de la citrouille :

Puis, nous avons défini la fonction corps_citrouille(x,y,pen,fi) pour dessiner le corps de la citrouille. Celle-ci prend en paramètre la couleur et les coordonnées du corps. Le corps est constitué de plusieurs cercles assemblés dans un ordre précis pour que le remplissage s’effectue correctement.

# trace le corps de la citrouille
def corps_citrouille(x,y,pen,fi):
  pencolor(pen)
  pensize(5)
  fillcolor(fi)
  a = x
  b = y
  for i in range(2):
    up()
    goto(x,y)
    down()
    begin_fill()
    circle(70)
    end_fill()
    x += 30
    y -= 5

  a = x + 50
  for i in range(2):
    up()
    goto(a,b)
    down()
    begin_fill()
    circle(70)
    end_fill()
    a -= 30
    b -= 5

  goto(a + 5,b)
  begin_fill()
  circle(70)
  end_fill()

Nous appelons cette fonction avec des coordonnées et des couleurs différentes pour créer nos quatre corps de citrouille.

corps_citrouille(-380,110,"#E1C40C","#E3E013")
corps_citrouille(280,110,"#FF4017","#FF5733")
corps_citrouille(280,-260,"#502D9B","#6445A6")
corps_citrouille(-380,-260,"#26b510","#7EEb1E")

Résultat:

La tige de la citrouille :

De plus, nous avons conçu la tige à l’aide d’une autre fonction qui prend en compte les coordonnées et les couleurs de la tige. Pour la concevoir, nous avons d’abord tracé deux traits partant d’un même point puis nous les avons reliés pour effectuer le remplissage.

#  trace la tige de la citrouille
def tige_citrouille(x,y,pn,fl):
  setheading(0)
  up()
  goto(x,y)
  down()
  pencolor(pn)
  fillcolor(fl)
  begin_fill()
  right(160)
  for i in range(2):
    left(110)
    forward(30)
  goto(x,y)
  end_fill()
  begin_fill()
  right(20)
  for i in range(35):
    forward(1)
    right(2)
  end_fill()

Nous appelons ensuite cette fonction tige_citrouille(x,y,pn,fl) avec différentes coordonnées et couleurs pour bien positionner nos quatre tiges de citrouille.

tige_citrouille(-350,260,"#515146","#7A7A65")
tige_citrouille(310,260,"#0E6F12","#1B801F")
tige_citrouille(310,-110,"#8C0F37","#AD2953")
tige_citrouille(-350,-110,"#909207","#C2BD34")

Résultat :

Le visage :

Enfin, nous avons paramétré une fonction afin de lui créer un visage qui a pour arguments ses coordonnées. Le visage est composé de deux yeux qui sont des triangles, d’un nez qui est un triangle plus petit et d’une bouche également composée de formes géométriques.

# trace le visage de la citrouille
def visage(x,y):
  setheading(0)
  pensize(1)
  up()
  goto(x,y)
  down()
  pencolor("#000000")
  fillcolor("#000000")
  begin_fill()

# trace les deux yeux  
  for i in range(2):
    up()
    goto(x,y)
    down()
    for i in range(3):
      forward(30)
      left(120)
    x += 70
  end_fill()

# trace le nez 
  up()
  goto(x - 100,y - 22)
  down()
  begin_fill()
  for i in range(3):
    forward(22)
    left(120)
  end_fill()

# trace la bouche
  up()
  goto(x - 120,y - 35)
  down()
  begin_fill()
  l = 10
  for i in range(2):
    forward(l)
    right(45)
    forward(12)
    left(90)
    forward(12)
    right(45)
    l = 12
  forward(15)
  for i in range(90):
    forward(0.5)
    right(2)
  for i in range(2):
    right(45)
    forward(12)
    left(90)
    forward(12)
    right(45)
    forward(12)
  right(45)
  forward(12)
  left(90)
  forward(12)
  right(45)
  for i in range(90):
    forward(0.5)
    right(2)
  end_fill()

Nous devons donc appeler cette fonction visage(x,y) avec des coordonnées différentes pour aligner le visage sur le corps de chaque citrouille.

visage(-378,193)
visage(282,193)
visage(282,-177)
visage(-378,-177)

Résultat :

Les difficultés rencontrées :

La première difficulté a été de trouver la distance entre les lettres pour former l’expression « HAPPY HALLOWEEN ». Au début, nous les avions espacées de 30 pixels puis nous nous sommes aperçues que certaines lettres se chevauchaient. Après de nombreuses tentatives, nous sommes arrivées au rendu recherché.

Une autre difficulté a été de tracer les cercles qui composent le corps de la citrouille dans le bon ordre pour que le remplissage s’effectue correctement. En ayant compris la logique du remplissage nous sommes finalement parvenues à trouver cet enchaînement.

Enfin, nous avons dû trouver les emplacements de tous les éléments qui composent la citrouille (le corps, la tige et le visage). Ce processus a été très long et a nécessité de nombreux essais mais nous y sommes arrivées !

Sources :

Pour écrire ce script, nous avons utilisé nos connaissances ainsi que le site python.org pour trouver de nouvelles commandes en python. Nous avons également regardé des projets de l’année dernière comme ceux qui sont dans la catégorie art.

Image finale :

Voici le rendu final de notre projet après avoir fait des tests, corrigé nos erreurs et pris du plaisir à coder !

Télécharger le script en .py :

Ne vous inquiétez pas, nous avons pensé à vous ! Vous pouvez télécharger le script ci-dessous pour vous amuser à le modifier ou tout simplement le montrer à vos amis !