Vous avez un devoir maison de SNT ou de NSI ? Vous vous intéressez à l’art génératif ? Vous êtes au bon endroit ! Plus besoin d’écumer Internet à la recherche d’informations : voici un condensé de trucs et astuces sur le module Turtle pour tous niveaux pas à pas.
Turtle, une bibliothèque de Python
Tout d’abord, il faut savoir ce que l’on utilise. Turtle est donc une bibliothèque graphique dans le langage de programmation Python. Elle contient donc un ensemble de modules et de fonctions pré-écrites qu’il est possible de réutiliser à volonté dans chaque programme.
Au début de chaque programme utilisant des fonctions de la bibliothèque Turtle, il est donc nécessaire d’importer la bibliothèque (par convention en tout début de programme) avec cette ligne :
from turtle import *
En cas d’oubli, un message d’erreur s’affichera dès que vous utiliserez une fonctionnalité propre au module.
Utiliser un IDE
Pour commencer à coder, vous avez besoin d’utiliser un IDE (de l’anglais Integrated Development Environment), un logiciel regroupant différents outils nécessaires à la création de programmes informatiques.
Pour ma part, j’utilise Thonny, qui a pour avantage d’être facile à utiliser, et de permettre de repérer facilement les erreurs. Voici donc une petite parenthèse sur l’utilisation d’une IDE, qu’il est possible de sauter pour les personnes ayant déjà codé.
Vous pouvez installer Thonny sur ordinateur ici. Le lien vous mène à la page suivante :
Il vous faudra cliquer sur Windows, Mac ou Linux selon votre système d’exploitation.
Après avoir installé l’application, vous devez tomber sur cet écran (ou dans une version en blanc, modifiable dans les paramètres).
Grosso – modo, l’écran se divise en deux parties :
Les codes s’écrivent dans le rectangle indiqué par la flèche bleue, tandis que la console d’exécution est indiquée par la flèche rouge. Dans le cas d’un programme Python utilisant Turtle, une fenêtre à part s’ouvre lors de l’exécution du code.
Par défaut, la Tortue se trouve au milieu, aux coordonnées (0,0)
Des commandes de base
Le concept de Turtle est simple : une « tortue », généralement symbolisée par une flèche se déplace sur l’écran et laisse des traces sur son passage. Le code permet donc de générer des formes géométriques, des dessins…
Bien évidemment, il existe des dizaines et des dizaines de commandes dans la bibliothèque Turtle. Il est cependant possible de résumer la base en quatre commandes : avancer, reculer et tourner.
forward() #avancer
backward() #reculer
left() #tourner à gauche
right() #tourner à droite
Entre les parenthèses se mettent les paramètres des fonctions. Concrètement, le programmeur y écrit donc les valeurs qu’il souhaite assigner à la fonction. Pour forward (et pour sa réciproque backward), cette valeur correspondra à la distance en pixels que la tortue avance (ou recule), tandis que pour left (et également right), ce sera l’angle en degré que la tortue tourne.
A l’aide des commandes, nous allons essayer de tracer un carré. Retour en primaire, comment se trace un carré ? Il suffit de tracer quatre traits, et de tourner à chaque fois de 90°. Cela donne donc :
from turtle import * #importe le module
forward(50) #avance de 50px
left(90) #tourne à gauche de 90°
forward(50)
left(90)
forward(50)
left(90)
forward(50)
left(90)
Ou encore, comme nous répétons la même action quatre fois, nous pouvons le réduire ainsi :
from turtle import *
for i in range(4): #boucle répétant 4 fois ce qu'il y a incrémenté ci dessous
forward(50)
left(90)
Cela donne donc :
Les couleurs
Les couleurs en Python peuvent être changées de différentes manières. La couleur par défaut de la tortue est noire. La fonction color() permet de la changer, avec entre parenthèse la nouvelle couleur.
Principalement, il est possible d’utiliser le système rgb, autrement dit Red Green Blue. Comme vous le savez peut-être, chaque couleur perçue par l’oeil humain est en réalité le mélange de trois couleurs primaires, le rouge, le vert et le bleu. L’écran d’un ordinateur, d’un téléphone etc… reproduit ce principe naturel. Le système rgb assigne donc des valeurs à chacune des trois couleurs, allant de 0 (couleur absente) au maximum 255.
Par exemple, la couleur que nous appelons le rouge, peut être transmis à une machine par 255, 0, 0, ce qui veut dire que l’intensité du rouge est maximale, tandis que le vert et le bleu sont inexistants. Le violet, mélange de rouge et de bleu, peut être codé comme 150, 0, 150, soit une présence importante de rouge et de bleu, mais pas de vert.
Personnellement, j’utilise ce site, qui permet de chercher en détail la couleur souhaitée, ou encore ce site, qui génère facilement des palettes de teintes proches.
Exemple :
from turtle import *
colormode(255) #initialise le système de couleur en mode rgb
color(215, 42, 42) #défini la couleur de la tortue, et donc de son tracé
for i in range(4): #trace le carré
forward(50)
left(90)
Le tracé de la tortue n’est donc plus noir, la couleur par défaut, comme dans le premier exemple, mais est maintenant rouge.
Turtle permet également l’utilisation d’un certain nombre de couleurs, en les appelant par leur nom en anglais.
from turtle import *
color("blue")
for i in range(4):
forward(50)
left(90)
Le tracé est donc maintenant bleu (blue en anglais). Il est important de noter que la couleur utilisée ainsi doit être entre guillemets, sans quoi l’exécution déclenche une erreur.
Des commandes des couleurs
Si vous désirez colorer l’intérieur d’une figure géométrique, il vous est possible d’utiliser la fonction fillcolor(), avec la couleur du remplissage en argument. Il faudra également préciser le début du remplissage avec begin_fill(), et la fin avec end_fill().
from turtle import *
fillcolor("blue") #sélection de la couleur bleu
begin_fill() #début du remplissage
for i in range(4):
forward(50)
left(90)
end_fill() #fin du remplissage, après avoir tracé le carré
De plus, il est possible de choisir une couleur de fond, celle par défaut étant le blanc. Pour cela il est possible d’utiliser bgcolor(), avec la couleur en argument.
from turtle import *
bgcolor("red") #la couleur du fond définie comme étant rouge
fillcolor("blue")
begin_fill()
for i in range(4):
forward(50)
left(90)
end_fill()
Pas très agréable à regarder, je vous l’accorde, mais il est donc facile de remplir le fond ou des formes géométriques en Python.
Les commandes les plus utiles
Il est possible de parler et d’expliquer toutes les fonctionnalités du module durant toute la nuit. Le tableau ci dessous est donc simplement un condensé de celles que je trouve personnellement les plus utiles. Il est important de noter que toutes les fonctions suivantes sont compatibles avec la calculatrice Numworks, sauf indication contraire.
La fonction
Qu’est ce qu’elle fait ?
Commentaire
goto()
Déplace la tortue vers les coordonnées (x,y) entrées en paramètre.
Cette fonction déplace seulement la tortue, et ne la téléporte pas, il y aura donc une trace de son passage.
penup()
Lève le « stylo » : la tortue ne laisse pas de trace sur son passage.
pendown()
Baisse le « stylo » : la tortue laisse une trace de son passage.
Il peut être utile, en le combinant avec goto() et penup(), d’en faire une nouvelle fonction permettant un déplacement sans trace. Ceci réduit les lignes et améliore la lisibilité.
speed()
Règle la vitesse de la tortue.
La vitesse la plus rapide à rentrer dans les paramètres est 0. Pour le reste des vitesses, elles vont de 1 (lent) à 10 (rapide).
circle()
Trace un cercle de rayon précisé en paramètre.
write()
Ecrit le texte mis en argument à l’emplacement de la Tortue.
Attention à bien mettre le texte entre guillemets !
pensize()
Défini la taille du « stylo » en pixel.
position()
Renvoie la position (x,y) de la Tortue.
Elle peut sembler peu utile à première vue, mais est un outil important une fois utilisé intelligemment, notamment pour des dessins complexes.
heading()
Renvoie l’orientation de la Tortue.
setheading()
Oriente la Tortue du degré mis en paramètre.
showturtle()
Rend la Tortue visible.
Durant l’étape de la conception du dessin, il est utile de pouvoir voir la Tortue.
hideturtle()
Masque la Tortue.
La Tortue est visible par défaut, il est important de penser à ce que cela ne soit pas le cas à la fin (à moins de l’intégrer dans le dessin)
Screen().setup()
Défini la taille de l’écran sur lequel se trouve le dessin, avec (longueur, hauteur) en paramètre.
Pas compatible avec la calculatrice Numworks, qui possède déjà une taille d’écran fixée.
reset()
Réinitialise le dessin.
Un petit programme (encore un fois horrible esthétiquement), qui rassemble une majorité des codes ci dessous.
from turtle import *
Screen().setup(400,200) #défini la taille de l'écran à 400px sur 200px
hideturtle() #cache la tortue
pensize(5) #défini la taille du stylo à 5px
speed(10) #défini une vitesse rapide
penup() #lève le stylo
goto(10,-60) #va a la position (10, -60)
pendown() #baisse le stylo
color("green") #la couleur de la tortue est verte
circle(40) #trace un cercle de rayon 40px
penup() #lève le stylo
goto(-40,20) #va a la position (-40, 20)
pendown() #baisse le stylo
write("Hello, world") #écrit le texte Hello, world
Je vous invite cependant à essayer toutes les fonctions, puisque seule la pratique et la recherche à tâtons vous permettront de vraiment vous les approprier.
D’autres astuces ?
N’hésitez pas à utiliser d’autres modules pour la réalisation de votre dessin. Le module random, notamment, permet de réaliser des motifs placés aléatoirement, ou de tailles ou de couleurs aléatoires. Les étoiles dans le ciel, par exemple, sont facilement réalisables par ce moyen.
Il est également possible d’utiliser l’itération de la boucle for i in range pour produire des dégradés faciles (voir le code ci dessous), ou augmenter progressivement une taille etc…
Les limites du codage sont infinies, mais ce sont ici quelques astuces que j’ai retenu de ma propre expérience dans l’art génératif.
Un résultat
Voici donc un court code qui emploi uniquement les commandes du tutoriel (hormis les variables et les boucles qui ne sont pas propres au module Turtle) :
from turtle import *
#paramètres de départ
Screen().setup(340,240)
colormode(255)
speed(0)
#le fond
cote = 1
for i in range(160):
color(30+i,30,80+i)
for i in range(4):
forward(cote)
right(90)
cote+=0.5
penup()
goto(0,0)
pendown()
#la rosace
x = 100
col = 50
for i in range(7):
color(col,col,col)
for i in range (10):
circle(x)
left(360/10)
x-=10
col+=25
N’hésitez pas à le reprendre pour changer les variables, les couleurs etc… Le hasard est parfois le meilleur moyen d’arriver à un magnifique résultat !
Conclusion
« Les frontières du possible sont tracées par ce que l’imaginaire ose »
André Malraux
A vos ordinateurs ! Toutes les leçons du monde ne remplaceront jamais l’expérience. Ainsi, la curiosité, l’entrainement et la pratique restent le meilleur moyen de progresser en codage. Le tutoriel devrait cependant vous permettre de faire les premiers pas en art génératif !
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.