Le jeu de paires est un jeu de société qui est connu sous différents noms. Nous vous proposons ici une version revisitée pour réviser ses capitales européennes !
Gameplay
Ce jeu vous propose de tester vos connaissances géographiques.
Vous devrez associer 12 pays à leurs capitales respectives.
Les noms des pays et des capitales sont mélangés aléatoirement.
Votre mission est de reconstituer les bonnes paires.
Réfléchissez bien et tentez de faire un sans-faute !
Amusez vous tout en apprenant de nouvelles choses.
Prêt à relever le défi ?
Capture d’écran
Commandes
◁ △ ▷ et ▽
OK
Déplacer le curseur
Sélection de la carte
Télécharger
Voici les liens pour télécharger le code python du jeu pour la NumWorks :
Le jeu de cartes incontournable arrive sur votre NumWorks ! L’objectif du Blackjack est clair : battre le croupier en formant une main dont la valeur est la plus proche possible de 21, sans jamais la dépasser. Tirez des cartes, restez stratégique et découvrez l’excitation de ce classique adapté à votre calculatrice !
Fonctionnalités
Simulation complète des règles du Blackjack
Mode solo contre un croupier virtuel
Affichage clair des cartes et des points
Prise en charge des commandes intuitives sur NumWorks
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.
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.
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 :
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.
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.
Bill Cipher est un personnage fictif de la série animée « Gravity Falls ». C’est un démon triangulaire jaune avec un chapeau haut de forme, connu pour ses pouvoirs surnaturels et son rôle antagoniste. Il est souvent représenté comme une figure maléfique et manipulatrices. Comme nous aimons tout les deux ce personnage, nous avons voulu le représenter en python. Voici ci dessous l’image d’origine, tiré du cartoon, que nous avons voulu reproduire en python.
Structure globale du script
Pour que ce soit plus clair à expliquer et pour nous même, nous avons d’abord mis toutes les fonctions, puis les avons exécutés une par une dans l’ordre nécessaire au bon fonctionnement du script. Dans l’ordre, nous avons les fonctions : -cercle_centre(x) : trace un cercle qui a pour centre le point 0, 0 et un rayon de taille x pixels. -lignes1(inner_radius, outer_radius, num_segments) : trace un certain nombre de lignes entre un rayon extérieur et intérieur d’un cercle, partant de 0, 0 et ayant pour rayon inner_radius et outer_radius. Entre chaque lignes, rajoute un symbole d’UTF 8 pour remplacer les symboles présents dans le dessin originel. -lignes2(inner_radius, outer_radius, num_segments) : Même principe, mais sans les symboles en UTF 8. -Bill(1) : Fais l’image de Bill Cipher, au centre de l’image. La variable 1 permet de choisir la taille du triangle principale de bill, cette fonction permet simplement de construire Bill à un endroit de l’image.
Les fonctions du script expliquées en détail : cercle_centre
Ici, la fonction déplace la tortue x pixel en dessous du point 0, 0 (la deuxième coordonné représente l’ordonné). On est donc à une distance de rayon du centre de l’image, ce qui fait qu’on a un cercle avec un centre 0, 0.
Les fonctions du script expliquées en détail : lignes1
def lignes1(rayon_intérieur, rayon_extérieur, num_segments):
angle = 360 / num_segments
symboles = ["☣", "☯", "Ω", "♔", "☀", "☢", "∞", "⚛", "✴", "♧"]
for i in range(num_segments):
penup()
goto(0, 0)
setheading(i * angle)
forward(rayon_intérieur)
pendown()
forward(rayon_extérieur - rayon_intérieur)
penup()
goto(0, 0)
# Dessiner le symbole
setheading(i * angle + angle / 2)
# Adjust the position to move lower symbols down
if i >= 5: # For example, if you want to move the last 5 symbols down
forward((rayon_intérieur + rayon_extérieur) / 2 - -20) # Move down more
else:
forward((rayon_intérieur + rayon_extérieur) / 2 - 20) # Move down less
write(symboles[i], align="center", font=("Arial", 30,))
goto(0, 0)
Les paramètres de la fonction sont le rayon intérieur, le rayon extérieur du cercle et le nombre de segments que l’on veut. angle = 360 / num_segments : cette ligne calcule l’angle entre chaque segment en degrés, en divisant le cercle complet (360 degrés) par le nombre de segments désiré. On a rangé les symboles dans une liste, cela permet d’en utiliser un différent à chaque fois grâce à la variable i. Dans le for i in range(num_segments), on commence par multiplier l’étape de la boucle à laquelle on est (i) par l’angle entre chaque segments. On relève le stylo, on avance jusqu’au rayon intérieur, on rabaisse le stylo, et enfin on avance de la différence entre le rayon extérieur et intérieur (ce qui correspond à la distance qu’il reste à parcourir). Le setheading permet de pointer la tortue vers le centre de la case, angle/2 étant le milieu de la case. Pour les symboles de 0 à 5, les symboles supérieurs, le stylo descend légèrement avant de placer le symboles (elle est tourné vers le bas grâce au setheading). Pour les symboles de 0 à 4, ils sont placés plus haut. Enfin, on écris le symbole qui correspond à l’étape à laquelle on est (stockée dans i). On aligne les symboles avec la tortue, puis on défini la police.
lignes1(370, 440, 10)
Les fonctions du script expliquées en détail : lignes2
def lignes2(rayon_extérieur, num_segments):
angle = 360 / num_segments
for i in range(num_segments):
penup()
goto(0, 0)
setheading(i * angle)
distance = rayon_extérieur
while distance > 0:
distance_mouvement = min(25, distance)
forward(distance_mouvement)
distance -= distance_mouvement
if random.random() < 0.5:
penup()
else:
pendown()
penup()
goto(0, 0)
Le premier paragraphe fait la même chose que dans lignes1. Dans le second paragraphe, on commence à définir la distance à parcourir, qui est égale au rayon extérieur (puisque l’on part du point 0, 0 le rayon intérieur est inutile). Tant qu’il est plus grand que 0, on avance de la plus petite valeur entre 25 et distance (qui est défini par distance_mouvement). Ensuite, on fait la différence entre distance et distance_mouvement. Cela permet d’avancer de 25 pixel à chaque fois, sauf à la fin où l’on avance de pile le nombre de pixel avant d’atteindre la limite. Tout les 25 pixels avancés, le script baisse ou lève aléatoirement le stylo, ce qui donne une impression d’irrégularité aux lignes (elles peuvent faire 0, 25, 50, 75, etc… pixels avant que le stylo ne se lève ou ne se baisse). Enfin, on repart du point 0, 0 et on recommence avec un angle différent.
pensize(1), lignes2(360, 100)
Les fonctions du script expliquées en détail : lignes3
def lignes3(rayon_intérieur, rayon_extérieur, num_segments):
angle = 360 / num_segments
for i in range(num_segments):
penup()
goto(0, 0)
setheading(i * angle)
forward(rayon_intérieur)
pendown()
forward(rayon_extérieur - rayon_intérieur)
penup()
goto(0, 0)
C’est la même chose que dans lignes1, mais sans les symboles.
lignes3(450, 1200, 300)
Les fonctions du script expliquées en détail : bill
y = 1
def bill(size_bill):
global y
pensize(4)
goto(-200, -150) # Partie 1
setheading(0)
# Set the fill color
fillcolor("#f4e4c2") # Change "blue" to any color you want
begin_fill() # Start filling the shape
pendown()
for i in range(3):
forward(400 * size_bill)
left(120)
end_fill() # End filling the shape
penup()
# Partie 2
goto(0, 195), setheading(0), pensize(6), forward(-60), pendown(), forward(120), penup(), goto(-20, 195), setheading(90), pendown()
for i in range(40): # Partie 3
forward(150)
y = y + 1
goto(-20 + y, 195)
penup(), goto(-125, -20), setheading(0), pensize(4), pendown(), forward(250), penup(), goto(-160, -80), pendown(), forward(320), penup()
goto(-185, -130), pendown(), forward(370), penup(), setheading(270), goto(-70, -20), pendown(), forward(60), penup(), goto(70, -20), pendown()
forward(60), penup(), goto(-110, -80), pendown(), forward(50), penup(), goto(110, -80), pendown(), forward(50), penup(), goto(0, -80)
pendown(), forward(50), penup(), goto(-195, -150), setheading(0), pendown(), pensize(1)
for i in range(95):
b = randint(3, 5)
forward(b), left(90), forward(b**2), right(180), forward(b**2), setheading(0)
forward(4), left(90), forward(4**2), penup(), pensize(4), goto(-45,-113), pendown()
fillcolor("black")
begin_fill()
right(60)
for i in range(3):
forward(65)
left(120)
end_fill()
penup()
goto(45,-48)
setheading(270)
pendown()
fillcolor("black")
begin_fill()
right(60)
for i in range(3):
forward(65)
left(120)
end_fill()
penup()
goto(-65,60)
setheading(0)
pendown()
right(60)
for i in range(45):
left(2.6)
forward(3.5)
setheading(180)
right(60)
for i in range(45):
left(2.6)
forward(3.5)
penup()
goto(0,35)
setheading(90)
right(35)
pendown()
for i in range(28):
forward(2)
left(2.5)
setheading(90)
right(215)
for i in range(28):
forward(2)
left(2.5)
penup(), goto(0,23), setheading(270), pendown(), forward(5), penup(), goto(-35,32), setheading(225), pendown(), forward(13), penup()
goto(39,33), setheading(300), pendown(), forward(12), penup(), goto(0,97), setheading(90), pendown(), forward(13), penup(), goto(-30,92)
setheading(110), pendown(), forward(17), penup(), goto(30,92), setheading(70), pendown(), forward(17), penup(), goto(-95,-150)
setheading(265), pensize(6), pendown()
forward(90)
for i in range(20):
forward(2)
left(3)
forward(50)
setheading(260)
p=8
for i in range(10):
pensize(p)
forward(3)
p = p - 0.3
penup()
goto(95,-150)
setheading(275)
pensize(6)
pendown()
forward(90)
for i in range(20):
forward(2)
right(3)
forward(50)
setheading(280)
p=8
for i in range(10):
pensize(p)
forward(3)
p = p - 0.3
penup()
goto(-133,-33)
setheading(225)
pendown()
for i in range(40):
forward(3)
right(2)
setheading(45)
pos_hand_l=pos()
pensize(7)
for i in range(28):#oe
forward(1)
left(3)#oe
penup()
goto(pos_hand_l)
setheading(210)
pendown()
for i in range(40):
forward(1)
right(2)
penup()
goto(pos_hand_l)
setheading(265)
pendown()
for i in range(30):
forward(1)
right(3)
penup()
goto(pos_hand_l)
setheading(300)
pendown()
for i in range(25):
forward(1)
right(3)
penup()
goto(133,-33)
setheading(315)
pensize(p)
pendown()
for i in range(40):
forward(3)
left(2)
setheading(135)
pos_hand_l=pos()
pensize(7)
for i in range(28): #oe
forward(1)
right(3)#oe
penup()
goto(pos_hand_l)
setheading(330)
pendown()
for i in range(40):
forward(1)
left(2)
penup()
goto(pos_hand_l)
setheading(275)
pendown()
for i in range(30):
forward(1)
left(3)
penup()
goto(pos_hand_l)
setheading(240)
pendown()
for i in range(25):
forward(1)
left(3)
Pour créer Bill, nous avons commencé par le triangle principal, que nous avons rempli pour superposer la ligne 2. Ensuite, nous avons dessiné le chapeau en augmentant le pensize() afin de reproduire l’effet de l’image de référence. Pour le sommet du chapeau, nous avons utilisé une boucle où un trait se forme, puis la tortue se déplace d’un pixel pour refaire un trait, ce qui permet d’optimiser le script et de personnaliser sa taille.
Ensuite, nous avons recréé les briques sur Bill par essais et erreurs, en testant différentes coordonnées et distances pour reproduire fidèlement la texture des briques. Pour dessiner les traits en bas de Bill, nous avons fait avancer la tortue d’une distance aléatoire entre 3 et 5, puis elle monte verticalement de cette même distance au carré, créant ainsi l’effet de l’image originale tout en facilitant la personnalisation du script.
Pour le nœud papillon, nous avons créé deux triangles remplis qui s’emboîtent pour reproduire l’effet du Bill original. Concernant l’œil, nous avons pris le temps de déterminer l’angle et le nombre exact de répétitions nécessaires pour former la forme circulaire de l’œil et de l’iris avec précision. Pour les cils, nous avons simplement ajusté les coordonnées pour obtenir les bonnes valeurs.
La jambe est un trait avec un pensize() plus large, et pour le genou, nous avons utilisé une répétition de forward() et de left(). Le pied a été réalisé avec une boucle où le pensize() diminue progressivement à mesure que la tortue avance, imitant ainsi l’effet de l’image d’origine.
Enfin, les bras ont été construits avec une simple boucle de forward() et de right(). Les mains ont été créées avec des boucles contenant différentes valeurs pour reproduire fidèlement les doigts de Bill. La fonction pos() a été utilisée pour revenir facilement au même point de départ pour chaque doigt, soit le milieu de la main.
Le mot-clé global dans le code Python permet de déclarer que la variable y utilisée dans la fonction bill fait référence à la variable globale y définie en dehors de la fonction. Cela signifie que toute modification apportée à y à l’intérieur de la fonction affectera la variable y dans l’espace de noms global.
Dans le cadre du projet Art génératif qui consiste a générer une image en pythona l’aide du module turtle, j’ai réalisé ma voiture favorite, la Subaru Impreza dans un thème néon. Je vous explique dans cet article la rélisation de ce projet
La légendaire Subaru Impreza
L’Impreza est une compacte du constructeur automobile japonais Subaru, apparue en 1992. Icône de la marque grâce à ses succès en championnat du monde des rallyes (trois titres pilotes et trois titres constructeurs), elle en est à sa cinquième génération depuis 2016.Avec un moteur d’une cylindrée de 2 litres et 211 chevaux (94/97) et 217 (98/00) pour la version européenne de première génération sous l’appellation GT, les premières Impreza qui deviendront les WRX peuvent afficher des caractéristiques allant jusqu’à 320 chevaux dans leur version STI. Depuis 2015, La Subaru WRX est devenu un modèle indépendant de la Impreza et suit son propre développement. De ce fait, le nom Impreza disparait peu à peu des références sportives remplacé par la WRX et à moindre mesure par la Subaru BRZ.
Avant meme de realiser
Appel et verification des modules
from turtle import *
from random import randint
try:
from PIL import Image
pillow_installed = True
except:
print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
print("https://nsi.xyz/py2png")
pillow_installed = False
titre = "Bastien.S"
title(titre+" | Subaru Impreza WRC")
setup(1920,1080)
colormode(255)
speed(0)
hideturtle()
flash = True
if flash:
wn = Screen()
wn.tracer(0)
Ici on appelle les fonctions turtle et random ou plutot uniqument randint. On verifie ensuite on verifie si l’utilisateur a bien PIL d’installer s’il ne l’a pas un code d’erreur s’affichera ainsi qu’un lien pour installer le module. On definie le titre de la fenetre ainsi que sa taille,le type de couleurs utiliséeds (ici R,G,B), on cache la tortue, et randons le script instantané.
Un script en calques
Comme pour tout dessin numérique nous avons besoins de differents calques. Un calque est une couche transparente sur laquelle des éléments graphiques peuvent être placés et manipulés indépendamment les uns des autres. Les differentes fonctions représentent nos calques voici les notres :
background()
batiments()
road()
subaru()
logo()
Background()
def background():
r = 20
g = 20
b = 20
hauteur = 540
x = 0
for i in range(50):
pensize(10),color(r,g,b),penup(),goto(-960,hauteur),pendown(),forward(1920)
hauteur = hauteur - 8
b = b + 1
r = r + 1
goto(-960,hauteur)
for i in range(37):
pensize(10),color(r,g,b),penup(),goto(-960,hauteur),pendown(),forward(1920)
hauteur = hauteur - 10
g = g + 2
r = r - 1
goto(-960,hauteur)
for i in range(90):
pensize(7),color(35,35,45),penup(),goto(-960,hauteur + 1),pendown(),forward(1950)
hauteur = hauteur - 7
Nous avons décomposer fond en 3 parties distinctes: degradé noir vers rose puis rose vers vert pour le ciel puis du gris foncé pour la route.
Pour creer nos degradés nous plaçons notre tortue tout en haut a gauche de notre fenetre puis nous la faisons avancer jusqu’a l’extremité droite. Ensuite on retourne a gauche et on va a la ligne du dessous en diminuant la valeur (hauteur), tout ca grace a la fonction goto. On modifie la couleur en augmantant ou diminuant les valeur (R,G,B) et on recommence. Pour creer la route on fait la meme chose mais sans changer la couleur
Pour creer ses batiments sombre rien de plus simple et d’aussi long. Nous avons utilisé les fonctions bengin_fill et end_fill, avec differéntes teintes de gris pour créer du relief
Pour realiser les bandes nous avons creer des listes contenants les differents parametres de celles ci (longueurs, angle, etc)
subaru()
def subaru():
color(0,150,250)
pensize(7)
for i in range(2):
penup()
goto(-330,-200)
pendown()
#roue arriere
left(106),forward(95),left(60),forward(30),left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25),left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40)
#arriere voiture
right(155),forward(75),right(55),forward(20),left(45),forward(45),right(45),forward(50),right(25),forward(40)
#aileron
left(160),forward(30),right(80),forward(30),right(100),forward(60),backward(60),left(100),forward(8),right(100),forward(60),backward(60),left(80),forward(5),left(10),left(90),forward(20),left(90),forward(50),left(90),forward(35),left(20)
#plafond
forward(20),left(30),forward(80),right(22),forward(65),right(24),forward(85),right(9),forward(185),right(17),forward(70),backward(20)
#parebrise
right(165),forward(70),left(5),forward(145),left(112),forward(95)
#capot
left(50),forward(140),right(15),forward(35),left(5)
#phare
right(55),forward(32),backward(32),right(85),forward(35),left(90),forward(25),left(80),forward(120),left(110),forward(25),left(65),forward(85),right(10)
#capot
left(155),backward(35),left(20),backward(140),left(18),forward(150),left(35),backward(10),forward(20),right(35),forward(110),right(50),forward(10),backward(5),left(50),forward(15),right(40),backward(110) ,forward(110),left(23),forward(130),right(20),forward(50),right(35)
#phare droit
forward(20),right(10),forward(10),right(100),forward(60),right(100),forward(20),right(75),forward(50),penup(),goto(-330,-200),right(2),pendown()
#roue arriere
left(106),forward(95),left(60),forward(30)
#dessus du peneu
left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25)
#jante
left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40),left(37),forward(70),left(65),forward(55),left(60),forward(70)
#bas de caisse
right(58),backward(25),forward(190)
#roue avant
left(86),backward(45),right(78),forward(33)
#jante
penup(),left(85),forward(90),left(170),pendown(),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),penup(),left(48),forward(84),pendown(),left(96),backward(33),left(78),forward(112)
#portiere
left(10),forward(50),right(20),forward(55),left(35),forward(95),left(60),forward(75),left(35),forward(85),left(30),forward(70),left(124),forward(220),backward(140),left(67),forward(100),backward(180),right(140),forward(80),left(73),forward(140)
#roue avant
left(86),forward(20),right(22),forward(56),right(67),forward(50),right(67),forward(6),right(94),forward(47),left(47),forward(40),left(26),forward(62),left(27),forward(42),left(64),forward(71),left(60),forward(50),left(125),forward(15),right(77),forward(115)
#parechock
backward(20),right(108),forward(215),left(120),forward(40),right(120),forward(140),right(115),forward(38),left(115),backward(110),forward(190)
#aeration parchock
backward(45),penup(),right(90),forward(7),pendown(),right(90),forward(190),left(120),forward(10),right(120),penup(),forward(40),pendown(),forward(60),left(95),forward(20),left(85),forward(50),left(55),forward(25),right(55)
penup(),forward(35),pendown(),right(60),forward(50),left(60),forward(130),left(60),forward(60),left(30),penup(),forward(7),pendown(),right(90),forward(45)
#parechock
right(90),backward(10),forward(42),right(90),forward(10),right(60),forward(20),right(120),forward(20),right(90),forward(17),right(90),forward(5),left(85),forward(50),right(86),forward(30),forward(160),left(5),forward(170),backward(170),right(5),backward(160),left(86),forward(40),right(25),forward(20),right(60),forward(57),right(50),forward(20),right(30),forward(40),backward(40),left(80),forward(30),right(85),forward(40),backward(25),left(85),forward(210),right(180),color(255,255,255),pensize(2)
Nous avons dit que les batiments etaient longs? Et bien ce n’est rien a coté de cette subaru. Cette subaru a été bètement dessiné
logo()
def logo():
pensize(4)
def etoile_logo_main():
left(10),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),right(10)
def etoile_logo():
left(10),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),right(10)
color(255,255,0)
for i in range(2):
xetoile = [740,760,680,625,710]
nbx = len(xetoile)
yetoile = [400,350,350,305,310]
nby = len(yetoile)
for i in range(5):
penup(),goto (xetoile[i%nbx],yetoile[i%nby]),pendown(),etoile_logo(),penup(),goto (560,380),pendown(),etoile_logo_main()
pensize(2),color(255,255,255)
Afin de réaliser le logo Subaru nous avons créé deux fonction qui sont aussi simple que la subaru etoile_logo_main() qui correspond a la plus grande étoile et etoile_logo() a la plus petite. Avec goto() on a placer les etoiles a différentes coordonées.
Fin de script
if flash:
wn.update()
image = getcanvas()
nom_du_fichier_sans_extension=titre+"_"+hex(randint(2**30+2**25,2**30+2**25+2**24-1))[2:]
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')
try:
psimage = Image.open(nom_du_fichier_sans_extension+".ps")
psimage.load(scale=2)
psimage_resized = psimage.resize((1280, 720))
psimage.save(nom_du_fichier_sans_extension+".png")
print(nom_du_fichier_sans_extension+".png", psimage.size, "sauvegardé dans le dossier")
except:
if not pillow_installed:
print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
print("https://nsi.xyz/py2png")
else:
print("Oops! - 'ghostscript' not installed- RTFM :")
print("https://nsi.xyz/py2png")
exitonclick()
Ce script permet generer l’image final en .ps puis a la convertir en .png avec un nom aléatoir evitant que l’image précdédemment généré soit écrasé.
Script complet
from turtle import *
from random import randint
try:
from PIL import Image
pillow_installed = True
except:
print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
print("https://nsi.xyz/py2png")
pillow_installed = False
titre = "Bastien.S"
title(titre+" | Subaru Impreza WRC")
setup(1920,1080)
speed(0)
hideturtle()
flash = True
if flash:
wn = Screen()
wn.tracer(0)
# ============== MON SCRIPT PYTHON ==============
colormode(255)
def logo():
pensize(4)
def etoile_logo_main():
left(10),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),right(10)
def etoile_logo():
left(10),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),right(10)
color(255,255,0)
for i in range(2):
xetoile = [740,760,680,625,710]
nbx = len(xetoile)
yetoile = [400,350,350,305,310]
nby = len(yetoile)
for i in range(5):
penup(),goto (xetoile[i%nbx],yetoile[i%nby]),pendown(),etoile_logo(),penup(),goto (560,380),pendown(),etoile_logo_main()
pensize(2),color(255,255,255)
def background():
r = 30
g = 30
b = 30
y = 540
x = 0
for i in range(50):
pensize(10),color(r,g,b),penup(),goto(-960,y),pendown(),forward(1920)
y = y - 8
b = b + 1
r = r + 1
goto(-960,y)
for i in range(37):
pensize(10),color(r,g,b),penup(),goto(-960,y),pendown(),forward(1920)
y = y - 10
g = g + 2
r = r - 1
goto(-960,y)
for i in range(90):
pensize(7),color(35,35,45),penup(),goto(-960,y + 1),pendown(),forward(1950)
y = y - 7
def batiments():
pensize(2)
penup()
goto(-960,-226)
pendown()
forward(100)
color(20,20,20),forward(360),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(620),end_fill(),left(90),backward(80),begin_fill(),left(90),forward(650),left(90),forward(120),left(90)
forward(40),right(70),forward(60),left(70),forward(592),end_fill(),backward(2),left(90),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(624),end_fill(),left(90),backward(260),color(30,30,30)
begin_fill(),left(90),forward(640),left(100),forward(240),left(80),forward(599),end_fill(),left(90),forward(240),begin_fill(),left(90),forward(640),right(100),forward(240),right(80),forward(599),end_fill()
left(90),forward(350),begin_fill(),left(90),forward(640),left(100),forward(240),left(80),forward(599),end_fill(),left(90),forward(240),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(620)
end_fill(),color(20,20,20),begin_fill(),left(90),forward(1),left(90),forward(350),right(45),forward(80),right(95),forward(60),left(50),forward(35),left(25),forward(50),right(115),forward(383),end_fill(),begin_fill()
left(180),color(25,25,25),forward(430),right(100),forward(80),right(80),forward(415),end_fill(),left(90),begin_fill(),color(15,15,15),forward(3),left(90),forward(250),right(90),forward(80),left(45),forward(20),right(45)
forward(15),left(90),forward(60),right(90),forward(100),right(90),forward(325),end_fill(),left(90),backward(20),color(25,25,25),begin_fill(),left(90),forward(230),right(95),forward(100),left(95),forward(110),right(80)
forward(100),right(55),forward(10),right(45),forward(342),end_fill(),begin_fill(),left(90),backward(15),left(90),color(10,15,15),forward(300),right(92),forward(120),right(88),forward(20),left(90),forward(20),right(90)
forward(275),end_fill(),color(25,25,25)
left(180),begin_fill(),forward(275),right(90),left(80),forward(20),right(65)
forward(200),right(15),forward(100),right(90),forward(360),end_fill(),left(90)
t = -200
u = -330
def subaru():
color(0,150,250)
pensize(7)
for i in range(2):
penup()
goto(u,t)
pendown()
#roue arriere
left(106),forward(95),left(60),forward(30),left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25),left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40)
#arriere voiture
right(155),forward(75),right(55),forward(20),left(45),forward(45),right(45),forward(50),right(25),forward(40)
#aileron
left(160),forward(30),right(80),forward(30),right(100),forward(60),backward(60),left(100),forward(8),right(100),forward(60),backward(60),left(80),forward(5),left(10),left(90),forward(20),left(90),forward(50),left(90),forward(35),left(20)
#plafond
forward(20),left(30),forward(80),right(22),forward(65),right(24),forward(85),right(9),forward(185),right(17),forward(70),backward(20)
#parebrise
right(165),forward(70),left(5),forward(145),left(112),forward(95)
#capot
left(50),forward(140),right(15),forward(35),left(5)
#phare
right(55),forward(32),backward(32),right(85),forward(35),left(90),forward(25),left(80),forward(120),left(110),forward(25),left(65),forward(85),right(10)
#capot
left(155),backward(35),left(20),backward(140),left(18),forward(150),left(35),backward(10),forward(20),right(35),forward(110),right(50),forward(10),backward(5),left(50),forward(15),right(40),backward(110) ,forward(110),left(23),forward(130),right(20),forward(50),right(35)
#phare droit
forward(20),right(10),forward(10),right(100),forward(60),right(100),forward(20),right(75),forward(50),penup(),goto(u,t),right(2),pendown()
#roue arriere
left(106),forward(95),left(60),forward(30)
#dessus du peneu
left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25)
#jante
left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40),left(37),forward(70),left(65),forward(55),left(60),forward(70)
#bas de caisse
right(58),backward(25),forward(190)
#roue avant
left(86),backward(45),right(78),forward(33)
#jante
penup(),left(85),forward(90),left(170),pendown(),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),penup(),left(48),forward(84),pendown(),left(96),backward(33),left(78),forward(112)
#portiere
left(10),forward(50),right(20),forward(55),left(35),forward(95),left(60),forward(75),left(35),forward(85),left(30),forward(70),left(124),forward(220),backward(140),left(67),forward(100),backward(180),right(140),forward(80),left(73),forward(140)
#roue avant
left(86),forward(20),right(22),forward(56),right(67),forward(50),right(67),forward(6),right(94),forward(47),left(47),forward(40),left(26),forward(62),left(27),forward(42),left(64),forward(71),left(60),forward(50),left(125),forward(15),right(77),forward(115)
#parechock
backward(20),right(108),forward(215),left(120),forward(40),right(120),forward(140),right(115),forward(38),left(115),backward(110),forward(190)
#aeration parchock
backward(45),penup(),right(90),forward(7),pendown(),right(90),forward(190),left(120),forward(10),right(120),penup(),forward(40),pendown(),forward(60),left(95),forward(20),left(85),forward(50),left(55),forward(25),right(55)
penup(),forward(35),pendown(),right(60),forward(50),left(60),forward(130),left(60),forward(60),left(30),penup(),forward(7),pendown(),right(90),forward(45)
#parechock
right(90),backward(10),forward(42),right(90),forward(10),right(60),forward(20),right(120),forward(20),right(90),forward(17),right(90),forward(5),left(85),forward(50),right(86),forward(30),forward(160),left(5),forward(170),backward(170),right(5),backward(160),left(86),forward(40),right(25),forward(20),right(60),forward(57),right(50),forward(20),right(30),forward(40),backward(40),left(80),forward(30),right(85),forward(40),backward(25),left(85),forward(210),right(180),color(255,255,255),pensize(2)
def road():
color(100,100,100),pensize(7)
for i in range(2):
liste_longueur_bandes1 = [2,10,6,10,60,150,]
liste_longueur_bandes2 = [1,4,7,16,75,220]
liste_longueur_bandes3 = [2,10,10,12,100,200]
liste_longueur_bandes4 = [1,4,8,15,25,97]
liste_longueur_distance_bandes = (1,3,6,9,20,40)
liste_angle_gauche = [15,20,25,30,43,85]
liste_angle1 = [90,50,45,35,45,85]
liste_angle2 = [90,135,135,155,155,133]
liste_angle3 = [90,70,40,20,25,45]
liste_angle4 = [90,105,140,150,135,97]
nblb1 = len(liste_longueur_bandes1)
nblb2 = len(liste_longueur_bandes2)
nblb3 = len(liste_longueur_bandes3)
nblb4 = len(liste_longueur_bandes4)
nbldb = len(liste_longueur_distance_bandes)
nba = len(liste_angle_gauche)
nba1 = len(liste_angle1)
nba2 = len(liste_angle2)
nba3 = len(liste_angle3)
nba4 = len(liste_angle4)
penup(),goto (-540,-226),pendown()
for i in range(6):
right(liste_angle_gauche[i%nba])
forward(liste_longueur_bandes1[i%nblb1])
left(liste_angle1[i%nba1])
forward(liste_longueur_bandes2[i%nblb2])
left(liste_angle2[i%nba2])
forward(liste_longueur_bandes3[i%nblb3])
left(liste_angle3[i%nba3])
forward(liste_longueur_bandes4[i%nblb4])
left(liste_angle4[i%nba4])
forward(liste_longueur_bandes1[i%nblb1])
penup()
forward(liste_longueur_distance_bandes[i%nbldb])
pendown()
left(liste_angle_gauche[i%nba])
color(255,255,255)
pensize(2)
#calque ===================================
background()
batiments()
road()
subaru()
logo()
#GENERER DES IMAGES AUTOMATIQUEMENT ==============
if flash:
wn.update()
image = getcanvas()
nom_du_fichier_sans_extension=titre+"_"+hex(randint(2**30+2**25,2**30+2**25+2**24-1))[2:]
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')
try:
psimage = Image.open(nom_du_fichier_sans_extension+".ps")
psimage.load(scale=2)
psimage_resized = psimage.resize((1280, 720))
psimage.save(nom_du_fichier_sans_extension+".png")
print(nom_du_fichier_sans_extension+".png", psimage.size, "sauvegardé dans le dossier")
except:
if not pillow_installed:
print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
print("https://nsi.xyz/py2png")
else:
print("Oops! - 'ghostscript' not installed- RTFM :")
print("https://nsi.xyz/py2png")
exitonclick()
Le 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.
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.
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.
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.
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.
Minecraft est un jeu vidéo de type aventure « bac a sable » (construction complètement libre) développé par le Suédois Markus Persson, alias Notch, puis par la société Mojang Studios. Il s’agit d’un univers composé de voxels et généré de façon procédurale, qui intègre un système d’artisanat axé sur l’exploitation puis la transformation de ressources naturelles. Cet article montre la conception graphique à travers l’exemple de l’univers du jeu.
Le Projet
Comme cité dans l’introduction, cet article a pour but de montrer en détails la réalisation de notre conception graphique de l’univers de Minecraft. Pour ce projet, nous avons donc utilisé le module turtle, ainsi que le module random. Le script permettant d’exporter l’image générée a aussi été utilisé.
Le Code, en détails
Le code, dans son ensemble, est organisé avec chaque élément découpé et distinguable :
Tout d’abord, commençons par importation des librairies, ainsi que l’appel des fonctions :
from turtle import *
from random import randint
try:
from PIL import Image
pillow_installed = True
except:
print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
print("https://nsi.xyz/py2png")
pillow_installed = False
titre = "Perspective - A Day In Minecraft"
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")
setup(1280, 720)
speed(0)
hideturtle()
flash = True
if flash:
wn = Screen()
wn.tracer(0)
On ajoute donc les librairies turtle, ainsi que random, puis, une partie du script qui permet d’« exporter une image générée par turtle » est utilisée afin de vérifier que l’utilisateur a bien installé le module PIL (PILLOW), sinon un message d’erreur s’affichera et lui donnera un lien pour installer le module. Après que la vérification soit terminée, on met en place la partie du script qui permet de nommer la fenêtre qui va s’afficher avec le rendu ainsi que sa taille. Enfin on ajoute le type de couleurs utilisées (RGB par exemple), la vitesse de la tortue, et on dissimule cette dernière.
Apres cette étape, nous pouvons enfin commencer a coder avec turtle.
pour la première étape, nous avons le fond :
import turtle
colormode(255)
def ciel():
penup()
# Valeurs initiales pour un bleu plus foncé
rciel = 30 # Valeur de rouge
gciel = 144 # Valeur de vert
bciel = 255 # Valeur de bleu
hauteur = -360
goto(-640, -358)
pendown()
# Durée totale pour le dégradé
total_steps = 720
while hauteur <= 360:
# Premier demi dégradé: Du bleu foncé au blanc
if hauteur <= 0:
r_incr = (255 - rciel) / (total_steps / 2) # Incrément de rouge
g_incr = (255 - gciel) / (total_steps / 2) # Incrément de vert
b_incr = (255 - bciel) / (total_steps / 2) # Incrément de bleu
else: # Deuxième demi dégradé: Du blanc au bleu clair
r_incr = (173 - 255) / (total_steps / 2) # Incrément de rouge vers bleu clair
g_incr = (216 - 255) / (total_steps / 2) # Incrément de vert vers bleu clair
b_incr = (230 - 255) / (total_steps / 2) # Incrément de bleu vers bleu clair
pencolor(round(rciel), round(gciel), round(bciel))
forward(1280)
hauteur += 1
goto(-640, hauteur)
# Incrémenter les valeurs de couleur
rciel += r_incr
gciel += g_incr
bciel += b_incr
Tout d’abord, le code commence configurer le mode de couleur pour utiliser le modèle RGB, où les valeurs des couleurs rouge, vert et bleu peuvent varier de 0 à 255. Une fonction nommée ciel est définie pour encapsuler tout le code de dessin. À l’intérieur de cette fonction, le stylo est levé au début, ce qui signifie que la tortue ne dessine pas lorsque elle se déplace à sa position initiale. Des variables sont initialisées pour représenter les valeurs de couleur du dégradé, avec une première couleur qui est un bleu plutôt clair. La tortue est ensuite déplacée à une position spécifique sur l’écran sans dessiner. Le code définit aussi une variable pour le nombre total d’étapes du dégradé, fixé à 720, ce qui représente le nombre d’itérations dans la boucle qui va dessiner le dégradé. Ensuite, une boucle commence et continuera tant que la hauteur de la tortue est inférieure ou égale à 360. Cette boucle est essentielle pour dessiner le dégradé de couleur. À chaque itération, il y a une condition qui vérifie si la tortue est dans la première moitié du dégradé (jusqu’à une hauteur de 0). Si c’est le cas, des incréments pour chaque composante de couleur sont calculés afin de passer du bleu foncé au blanc. Si la tortue est dans la seconde moitié du dégradé (au-dessus de 0), le code calcule de nouveaux incréments pour faire la transition du blanc vers une nuance de bleu clair. Dans chaque itération, la couleur actuelle du stylo est définie en fonction des valeurs calculées, puis la tortue se déplace en avant pour dessiner une ligne horizontale. Ensuite, la hauteur est augmentée pour passer à la ligne suivante, et la tortue est déplacée vers la nouvelle position. Enfin, à chaque étape de la boucle, les valeurs de couleur sont mises à jour en ajoutant les incréments précédemment calculés, permettant au dégradé de progresser progressivement à travers l’écran. Le résultat est un effet de ciel qui commence en bleu clair en haut, devient blanc au milieu, puis continue vers une couleur bleu clair en bas, recréant ainsi un effet visuel similaire à celui du ciel dans le jeu Minecraft.
Ensuite, la fonction créant les nuages est ajoutée :
def carre(x, y, size):
# Dessine un carré de taille donnée aux coordonnées spécifiées
penup()
goto(x, y)
pendown()
color("white")
begin_fill()
for _ in range(4):
forward(size)
right(90)
end_fill()
def unnuage(x, y, size, grid_width, grid_height):
# Dessine un nuage carré et homogène composé d'une grille de carrés
for row in range(grid_height):
for col in range(grid_width):
# Positionne chaque carré dans une disposition en grille
square_x = x + col * size
square_y = y - row * size
carre(square_x, square_y, size)
def lesnuages():
# Place des nuages carrés et homogènes avec de grandes distances entre eux
cloud_positions = [
(-500, 250), (-200, 300), (100, 310),
(400, 310), (0, 180)]
for x, y in cloud_positions:
unnuage(x, y, size=20, grid_width=10, grid_height=5) # Taille et dimensions de chaque nuage
# Appel des fonctions pour dessiner le ciel et les nuages
lesnuages()
Le code utilise la bibliothèque turtle de Python pour dessiner des nuages sous forme de carrés. La fonction carre(x, y, size) dessine un carré d’une taille spécifiée aux coordonnées (x, y). Elle commence par lever le stylo pour ne pas dessiner en se déplaçant, puis elle se positionne au point donné, abaisse le stylo, et définit la couleur de remplissage à blanc. Ensuite, elle dessine le carré en avançant et en tournant à droite à chaque coin, avant de terminer le remplissage. La fonction unnuage(x, y, size, grid_width, grid_height) crée un nuage en formant une grille de carrés. Elle utilise des boucles imbriquées pour parcourir les lignes et les colonnes de la grille, calculant les coordonnées de chaque carré en fonction de la taille spécifiée. Chaque carré est dessiné en appelant la fonction carre. Enfin, la fonction lesnuages() détermine où les nuages doivent être placés sur l’écran. Elle utilise une liste de positions prédéfinies pour placer plusieurs nuages avec un certain espacement entre eux. En appelant ces fonctions, le code dessine une série de nuages carrés et homogènes sur l’écran.
Pour la prochaine étape, nous coderons le soleil :
def draw_square(x, y, size,color):
# Fonction pour dessiner un carré de couleur donnée aux coordonnées spécifiées
penup()
goto(x, y)
pendown()
turtle.color(color)
begin_fill()
for _ in range(4):
forward(size)
right(90)
end_fill()
def soleil():
# Centre du soleil (carré jaune)
sun_size = 125 # Taille du carré central du soleil
sun_x = -sun_size / 2 # Position X pour centrer le soleil
sun_y = 250 # Position Y pour placer le soleil en haut de l'écran
draw_square(sun_x, sun_y, sun_size, 'yellow')
# Dessiner le soleil
soleil()
Ce code utilise également la bibliothèque turtle pour dessiner un soleil sous forme de carré coloré. La fonction draw_square(x, y, size, color) permet de dessiner un carré aux coordonnées (x, y) avec une taille spécifiée et une couleur donnée. Elle commence par lever le stylo pour éviter de dessiner en se déplaçant, puis elle se positionne à la coordonnée indiquée. Après avoir abaissé le stylo, elle définit la couleur du carré en utilisant turtle.color(color). Ensuite, elle commence à remplir la forme et dessine le carré en avançant et en tournant à droite de 90 degrés à chaque coin. Une fois le carré terminé, elle termine le remplissage avec end_fill(). La fonction soleil() est responsable du dessin du soleil, elle définit d’abord la taille du carré central (sun_size) à 125 pixels et calcule les coordonnées pour centrer le carré en soustrayant la moitié de sa taille de la position X (sun_x). La position Y (sun_y) est fixée à 250 pour placer le soleil en haut de l’écran. Elle appelle ensuite la fonction draw_square pour dessiner le carré représentant le soleil, en le remplissant de couleur jaune. Enfin, l’appel à soleil() exécute cette fonction, ce qui dessine un grand carré jaune en haut de l’écran, représentant le soleil.
Puis, nous voyons le script permettant de de dessiner les blocs d’herbe :
def herbe():
# Configuration de l'herbe pixelisée
penup()
grass_height = 360 # Moitié de la hauteur de l'écran (720 / 2)
pixel_size = 20 # Taille de chaque pixel (carré)
# Dessiner des "pixels" de différentes nuances de vert dans la moitié inférieure
for y in range(-360, 0, pixel_size):
for x in range(-640, 640, pixel_size):
# Choisir une nuance de vert aléatoire pour chaque carré
green_value = random.randint(200, 255) # Valeur de vert entre 100 et 255
color = "#00%02x00" % green_value # Couleur hexadécimale pour une teinte de vert
goto(x, y)
turtle.color(color)
begin_fill()
for _ in range(4):
forward(pixel_size)
right(90)
end_fill()
# Dessin de l'herbe pixelisée, de la végétation et des fleurs
herbe()
La fonction herbe() est responsable du dessin de l’herbe pixelisée. Elle commence par lever le stylo avec penup() pour éviter de dessiner en se déplaçant. Ensuite, elle définit la hauteur de l’herbe en spécifiant grass_height à 360, ce qui correspond à la moitié de la hauteur de l’écran (720 pixels). La variable pixel_size est définie à 20, ce qui détermine la taille de chaque « pixel » (carré) d’herbe. Le code utilise deux boucles imbriquées pour parcourir les coordonnées de la zone où l’herbe sera dessinée. La première boucle itère sur les coordonnées Y de -360 à 0 (la moitié inférieure de l’écran), et la seconde boucle itère sur les coordonnées X de -640 à 640, couvrant ainsi toute la largeur de l’écran. À l’intérieur de la boucle, une valeur de vert aléatoire est choisie pour chaque carré en utilisant random.randint(200, 255). Cela génère une nuance de vert qui sera plus claire, puisque les valeurs hexadécimales de vert vont de 200 à 255. La couleur hexadécimale est ensuite formatée en utilisant color = "#00%02x00" % green_value, ce qui crée une chaîne de caractères représentant une couleur verte dans le format hexadécimal (ex. #00CC00). Pour chaque pixel d’herbe, le curseur se déplace aux coordonnées (x, y) correspondantes, et la couleur est définie. Ensuite, le remplissage commence avec begin_fill(), et un carré est dessiné en avançant de pixel_size et en tournant à droite de 90 degrés à chaque coin. Une fois le carré terminé, end_fill() termine le remplissage de ce pixel.
Il ne reste plus que deux étapes, parmi celles-ci, on ajoute au code un script permettant de dessiner un arbre.
# Fonction pour dessiner un carré pour les blocs
def draw_block(t, size, color):
t.begin_fill()
t.color(color)
for _ in range(4):
t.forward(size)
t.left(90)
t.end_fill()
# Fonction pour dessiner un arbre style Minecraft
def draw_minecraft_tree(x, y):
block_size = 65 # Taille de chaque bloc pour un effet fidèle
# Tronc de l'arbre en 4 blocs verticaux
trunk = turtle.Turtle()
trunk.hideturtle()
trunk.speed("fastest")
trunk.penup()
trunk.goto(x, y)
trunk.pendown()
for i in range(4): # Tronc de 4 blocs de hauteur
draw_block(trunk, block_size, "saddle brown")
trunk.penup()
trunk.goto(x, y + (i + 1) * block_size)
trunk.pendown()
# Feuillage de l'arbre en blocs
foliage = turtle.Turtle()
foliage.hideturtle()
foliage.speed("fastest")
foliage.penup()
# Position des blocs de feuillage par rapport au tronc
foliage_positions = [
(-1, 4), (0, 4), (1, 4), # Rangée supérieure
(-1, 3), (0, 3), (1, 3), # Rangée au milieu
(-2, 3), (2, 3), # Côtés de la rangée au milieu
(-1, 2), (1, 2), (0, 2) # Rangée inférieure
]
for pos in foliage_positions:
foliage.goto(x + pos[0] * block_size, y + pos[1] * block_size)
foliage.pendown()
draw_block(foliage, block_size, "forest green")
foliage.penup()
# Dessiner l'arbre style Minecraft à gauche
draw_minecraft_tree(-500, -100)
draw_minecraft_tree(150, -100)
draw_minecraft_tree(600, -200)
La fonction draw_block(t, size, color) dessine un carré de couleur spécifiée avec une taille donnée. Elle commence le remplissage, définit la couleur, puis dessine les quatre côtés du bloc en tournant à gauche de 90 degrés après chaque côté, et termine le remplissage une fois le bloc dessiné. La fonction draw_minecraft_tree(x, y) est responsable du dessin d’un arbre aux coordonnées (x, y). Elle fixe la taille de chaque bloc à 65 pixels et crée un Turtle pour dessiner le tronc. Ce Turtle se positionne aux coordonnées données et dessine quatre blocs verticaux pour former le tronc en utilisant une couleur marron. Après avoir dessiné chaque bloc, le Turtle se déplace vers le haut pour la prochaine position. Un second Turtle, destiné au feuillage, est également créé. Il est configuré pour dessiner des blocs de feuillage verts autour du tronc, avec des positions définies dans une liste pour donner une forme arrondie. Pour chaque position de feuillage, le Turtle se déplace aux coordonnées calculées et dessine un bloc de couleur verte. Enfin, la fonction draw_minecraft_tree est appelée trois fois avec différentes coordonnées pour dessiner trois arbres à des endroits variés de l’écran. Ce code crée ainsi une représentation d’arbres en blocs de style Minecraft, composés de carrés pour le tronc et le feuillage.
Enfin, pour donner la touche finale qui va donner vie a notre conception graphique sur le thème de Minecraft, on ajoute un dernier script qui permet de dessiner un Creeper :
# Fonction pour dessiner un sous-bloc "pixel"
def draw_pixel(t, size, color):
t.begin_fill()
t.color(color)
for _ in range(4):
t.forward(size)
t.left(90)
t.end_fill()
# Fonction pour dessiner un bloc pixelisé avec un mélange de couleurs
def draw_pixelated_block(t, x, y, block_size, pixel_size, colors):
t.penup()
for row in range(block_size // pixel_size):
for col in range(block_size // pixel_size):
t.goto(x + col * pixel_size, y - row * pixel_size)
t.pendown()
color = colors[(row + col + (row % 2)) % len(colors)]
draw_pixel(t, pixel_size, color)
t.penup()
# Fonction pour dessiner un visage Creeper précis
def draw_creeper_face(t, x, y, pixel_size):
t.penup()
# Yeux (4 pixels chacun)
t.goto(x + pixel_size, y - 1 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + 2 * pixel_size, y - 1 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + pixel_size, y - 2 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + 2 * pixel_size, y - 2 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + 4 * pixel_size, y - 1 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + 5 * pixel_size, y - 1 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + 4 * pixel_size, y - 2 * pixel_size)
draw_pixel(t, pixel_size, "black")
t.goto(x + 5 * pixel_size, y - 2 * pixel_size)
draw_pixel(t, pixel_size, "black")
# Bouche (3x2 pixels pour la rangée supérieure, 2x1 pixels pour la rangée inférieure)
t.goto(x + 15, y - 4 * pixel_size)
for _ in range(4):
draw_pixel(t, pixel_size, "black")
t.forward(pixel_size)
t.goto(x + 25, y - 3 * pixel_size)
for _ in range(2):
draw_pixel(t, pixel_size, "black")
t.forward(pixel_size)
t.goto(x + 15, y - 5 * pixel_size)
for _ in range(4):
draw_pixel(t, pixel_size, "black")
t.forward(pixel_size)
t.goto(x + 45, y - 6 * pixel_size)
for _ in range(1):
draw_pixel(t, pixel_size, "black")
t.forward(pixel_size)
t.goto(x + 15, y - 6 * pixel_size)
for _ in range(1):
draw_pixel(t, pixel_size, "black")
t.forward(pixel_size)
# Fonction pour dessiner un Creeper Minecraft pixelisé complet
def draw_pixelated_creeper(x, y):
block_size = 70 # Taille de chaque section principale du Creeper
pixel_size = 10 # Taille de chaque pixel
# Couleurs pour le visage et le corps du Creeper
face_colors = ["#2E8B57", "#228B22", "#32CD32", "#006400"]
body_colors = ["#228B22", "#006400", "#32CD32"]
creeper = turtle.Turtle()
creeper.hideturtle()
creeper.speed("fastest")
# Tête du Creeper avec visage
draw_pixelated_block(creeper, x, y, block_size, pixel_size, face_colors)
draw_creeper_face(creeper, x, y, pixel_size)
# Corps du Creeper (2 blocs de hauteur sous la tête)
for i in range(2):
draw_pixelated_block(creeper, x, y - (i + 1) * block_size, block_size, pixel_size, body_colors)
# Pieds du Creeper (2 blocs de largeur sous le corps)
for i in range(2):
draw_pixelated_block(creeper, x - block_size // 2, y - (i + 3) * block_size, block_size // 2, pixel_size, body_colors)
draw_pixelated_block(creeper, x + block_size // 2, y - (i + 3) * block_size, block_size // 2, pixel_size, body_colors)
# Dessiner le Creeper pixelisé à droite
draw_pixelated_creeper(450, 50)
Et voila ! Nous voici avec une image qui représente (plus ou moins 😅) l’univers du jeu vidéo Minecraft, et cela, avec turtle et random !
Les difficultés du projet
Durant ce projet, nous avons eus quelques difficultés a savoir lors de la correction du code ou encore lorsqu’il fallait positionner certains éléments. Nous avons, bien que très peu, utilisés l’AI Chat GPT pour nous guider dans la correction ou encore quelques conceptions de script.
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.
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
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.
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.
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.
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é.
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) :
Pour ce premier projet de NSI, nous avons décider de prendre comme thème une image de la Patrouille de France, vous pourrez retrouver ici les démarches par lesquelles nous sommes passées.
Notre vidéo :
Le Fond :
Tout d’abord pour obtenir notre image nous avons créer un fond dégradé bleu :
n = 350
s = (0, 0, 139)
e = (173, 216, 230)
w, h = 1280, 720
def degrade(st, end, step, t_step):
return st + (end - st) * step / t_step
Pour obtenir ce résultat nous avons donc créer les paramètres de notre dégradé, il y en a 4 : le nombre de ligne que va faire la tortue (n) ; la couleur de départ (s) ; la couleur de fin (e) ; La longueur et la largeur (w, h) . Afin que la tortue change petit à petit de couleur, on créait la fonction degrade, qui va donc utiliser 3 paramètres : le nombre de ligne que va faire la tortue ; la couleur de départ ; la couleur de fin et effectuer une opération mathématique.’
for i in range(n):
r=int(degrade(s[0], e[0], i, n))
g=int(degrade(s[1], e[1], i, n))
b=int(degrade(s[2], e[2], i, n))
screen.colormode(255)
pen.color(r, g, b)
Ici, on calcule la nuance de bleu qui va être utilisé à chaque ligne du dégradé.
screen.colormode(255)
pen.color(r, g, b)
screen.colormode(255) : Cette fonction définit le mode de couleur de l’écran sur une échelle de 0 à 255. Par défaut, Turtle utilise une échelle de couleur de 0 à 1 (où chaque couleur est une valeur décimale entre 0 et 1).
pen.color(r, g, b) : Cette fonction définit la couleur du stylo en utilisant trois valeurs pour le rouge (r), le vert (g), et le bleu (b). Ces valeurs doivent être comprises entre 0 et 255.
pen.goto(-w/2, h/2 - i * (h / n))
pen.begin_fill()
for _ in range(2):
pen.forward(w)
pen.right(90)
pen.forward(h / n)
pen.right(90)
pen.end_fill()
Ici, on délimite le rectangle dans lequel le dégradé doit être créer.
Les avions :
Ensuite nous avons créer une fonction avion qui créer nos avions :
Tout d’abord, nous avons créé une fonction avion. Dans cette partie du code, nous dessinons la silhouette de l’avion à l’aide de goto(), nous lui donnons une couleur avec color(), puis nous la remplissons avec begin_fill() et end_fill().
Pour créer les traîners, nous avons simplement créé une fonction de base où nous avons changé la couleur.
Les problèmes rencontrés :
Lorsque nous avons voulu générer nos avions avec les traînées à l’arrière nous avons obtenue l’image suivante :
Le problème venait du fait que nous avions dessiner les avions avant de dessiner les traînées. Donc les traînées sont apparues par dessus les avions.
Nos sources :
Afin de réaliser le code nous avons utiliser le site python.org, ainsi que nos connaissances. Nous avons aussi pris le temps de regarder les travaux des années précédant de la catégories art.