Colorier

À travers cette partie, nous allons voir comment rajouter des couleurs dans nos dessins.

Couleur de tracé et de remplissage

Nous avons vu dans la première partie portant sur les configurations que nous pouvons changer la couleur de fond en passant soit une chaîne de caractères (nom de la couleur ou bien code hexadécimal) soit un tuple représentant le code (R, G, B) avec des valeurs entre 0 et 1 par défaut. À travers les fonctions utilisées dans cette partie, nous choisirons les couleurs de cette même façon.

Tout d’abord, il faut savoir que notre crayon n’a pas une, mais deux couleurs : une couleur pour tracer ainsi qu’une couleur pour remplir.

Couleur du tracé

Pour modifier, la couleur de tracé, nous pouvons utiliser la fonction pencolor avec la couleur voulue. Si nous ne lui passons aucun paramètre, elle nous retourne la couleur de tracé actuelle. Voici un exemple d’utilisation :

turtle.forward(100)  #Trace un trait noir de 100px
turtle.left(90)  #Tourne de 90°
turtle.pencolor("red")  #Change la couleur de traçage à rouge
turtle.forward(100)  #Trace un trait rouge de 100px
print(turtle.pencolor())  #Affiche 'red'

Nous pouvons nous amuser à faire un dégradé, comme l’illustre l’exemple ci-dessous. Dans celui-ci nous traçons une ligne pour chaque variation de rose allant de 0 à 255 inclus (que nous reportons sur une échelle de 1) en partant du bas et en remontant.

Le code :

import turtle

LARGEUR, HAUTEUR = 256, 256

if __name__ == "__main__":
    pos_y = -HAUTEUR/2  #On initialise la position en hauteur initiale
    turtle.setup(LARGEUR, HAUTEUR)  #On initialise la fenêtre
    turtle.speed(0)  #On met la vitesse de traçage la plus rapide
    #Pour val allant de 255 à 0 (couleur)
    for val in range(255, -1, -1):
        #On se déplace au début de la ligne à tracer
        turtle.up(); turtle.goto(-LARGEUR, pos_y); turtle.down()
        turtle.pencolor((val/255, 0, val/255)) #On prépare la couleur
        turtle.forward(LARGEUR)  #On trace la ligne
        pos_y += 1  #On remonte de 1px à chaque fois
    turtle.exitonclick()
Un dégradé de rose.
Un dégradé de rose.

Si vous le souhaitez, vous pouvez vous entraîner en créant vos propres variations de dégradé (en changeant de sens, de couleur, etc.).

Couleur de remplissage

De la même manière que pour le tracé, nous pouvons modifier ou connaître la couleur de remplissage avec la fonction fillcolor. Toutefois, nous devons préciser avant de commencer à tracer si nous voulons remplir la figure avec begin_fill. De même, une fois celle-ci finie, il nous faut le préciser avec end_fill pour que le remplissage devienne actif. Sinon, il ne sera pas visible.

Si nous ne fermons pas notre figure en retournant au point de départ, end_fill se charge de la jonction entre notre position actuelle et le point de départ, avec un trait de la même couleur que la couleur de remplissage. Voici deux exemples pour comprendre ce fonctionnement :

#Carré noir rempli de rouge
print(turtle.fillcolor())  #Affiche 'black'
turtle.fillcolor("red")  #Change la couleur de remplissage à rouge
turtle.begin_fill()  #Précise le début du remplissage
for i in range(4):
    turtle.forward(120)
    turtle.left(90)
turtle.end_fill()  #Précise la fin du remplissage
#Moitié d'octogone en vert (sauf le dernier trait) rempli de jaune
turtle.pencolor("green")  #Change la couleur de traçage à vert
turtle.fillcolor("yellow")  #Change la couleur de remplissage à jaune
turtle.begin_fill()  #Précise le début du remplissage
for i in range(4):
    turtle.forward(75)
    turtle.left(360/8)
turtle.end_fill()  #Précise la fin du remplissage
Résultats des exemples avec fillcolor.
Résultats des exemples avec fillcolor.
Changer les deux d’un coup

La fonction color nous permet de faire d’une pierre deux coups. En effet, nous pouvons lui passer la couleur de tracé ainsi que la couleur de remplissage. Si nous ne lui passons rien, elle nous retourne les valeurs actuelles. Si nous lui passons une seule valeur, alors la couleur de tracé et celle de remplissage prendront toutes deux celle-ci.

turtle.color("green")  #Équivalent de turtle.pencolor("green") et turtle.fillcolor("green")
turtle.color("red", "yellow")  #Équivalent de turtle.pencolor("red") et de turtle.fillcolor("yellow")
print(turtle.color())  #Affiche '('red', 'yellow')'

Vous remarquerez que nous pouvons connaître aisément ces informations en regardant notre crayon si celui-ci est affiché. En effet, la couleur de ses contours correspond à la couleur de tracé tandis que la couleur de son intérieur correspond à la couleur de remplissage.

Comme d’habitude, je vous encourage à tester ces fonctions avant de continuer. Nous allons maintenant créer notre propre fonction pour dessiner des points.

Notre fonction point

Si vous vous rappelez bien, nous avons vu la fonction dot, qui permet d’imprimer un point, au cours de la partie précédente. Or, avec ce que nous venons d’apprendre, nous sommes en mesure de la programmer ! En effet, un point n’est rien de plus qu’un cercle, d’un certain diamètre, tracé et rempli par une certaine couleur. Je vous rappelle que si nous ne passons aucune valeur à dot, la fonction affiche un point d’un diamètre par défaut et de la couleur de traçage actuel. Je vous rappelle aussi que le point est dessiné même si le crayon est levé, que le curseur est le centre du point, et que l’orientation n’influe pas sur le sens du traçage (pas comme avec circle). Avec ce que nous venons de dire, nous pouvons faire quelque chose comme :

Le code :

import turtle

def deplacer_sans_tracer(x, y = None):
    """Fonction pour se déplacer à un point sans tracer"""
    turtle.up()
    if (isinstance(x, tuple) or isinstance(x, list)) and len(x) == 2:
        turtle.goto(x)
    else:
        turtle.goto(x, y)
    turtle.down()

def point(diametre = 5, couleur = None):
    """Fonction pour dessiner un point"""
    #On récupère les valeurs courantes du crayon
    etat_crayon = turtle.isdown()
    orientation = turtle.heading()
    position_crayon = turtle.position()
    couleurs_crayon = turtle.color()

    #On se prépare pour dessiner le point
    '''Si une couleur est passée alors le point sera de cette couleur,
    sinon le point aura la même couleur que la couleur de traçage. On vérifie
    alors que la couleur de remplissage est bien la même que la couleur de
    traçage'''
    if couleur != None:
        turtle.color(couleur, couleur)
    elif couleurs_crayon[0] != couleurs_crayon[1]:
        turtle.fillcolor(couleurs_crayon[0])
        
    deplacer_sans_tracer(position_crayon[0], position_crayon[1]-diametre//2)
    turtle.setheading(0)
    
    if not etat_crayon:
        turtle.down()

    #On dessine le point
    turtle.begin_fill()
    turtle.circle(diametre//2)
    turtle.end_fill()

    #On remet les valeurs comme au début
    deplacer_sans_tracer(position_crayon)
    turtle.setheading(orientation)

    if couleur != None:
        turtle.color(couleurs_crayon[0], couleurs_crayon[1])
    elif couleurs_crayon[0] != couleurs_crayon[1]:
        turtle.fillcolor(couleurs_crayon[1])
    
    if not etat_crayon:
        turtle.up()
    turtle.setheading(orientation)
    
#On teste notre fonction
if __name__ == "__main__":
    point(200)
    turtle.up()
    point(170, 'red')
    turtle.left(180)
    turtle.color('grey', 'white')
    point(100)
    turtle.pencolor('yellow')
    point()
    turtle.exitonclick()
Résultat des tests avec la fonction point.
Résultat des tests avec la fonction point.

Vous pouvez vous amuser à remplacer les appels à point par des appels à dot et vous verrez que le résultat est le même ! Toutefois, notre version n’est pas parfaitement identique puisque le diamètre par défaut est sans doute choisi d’une manière différente. De plus, nous ne prenons pas en compte la valeur de la largeur du crayon. Si vous le souhaitez, vous pouvez donc améliorer notre fonction afin qu’elle ait le même comportement que dot vis à vis de la taille de traçage (retournée par pensize).

Notre remplissage personnalisé

Nous avons vu que si nous ne fermons pas notre figure alors la fonction de fin de remplissage se charge de la jonction. Toutefois, comme nous l’avons aussi vu, ce dernier trait a la même couleur que celle de remplissage. Or, il serait mieux qu’il ait la couleur de traçage afin que notre figure soit harmonieuse. Nous pouvons résoudre ce problème aisément et c’est ce que nous allons faire à travers cette section.

Pour mener à bien notre mission, nous allons utiliser quelques fonctions. Tout d’abord, la fonction begin_poly qui nous permet de commencer à enregistrer par quel(s) point(s) passe notre figure (la position actuelle incluse). Ensuite, la fonction end_poly qui met fin à cet enregistrement. Enfin, nous utiliserons aussi get_poly qui nous retourne un tuple de points et nous permet d’avoir accès à ce que nous venons d’enregistrer. Ces trois fonctions ne prennent aucun paramètre. Voici un petit exemple :

turtle.begin_poly()  #Commence à enregistrer
print(turtle.get_poly())  #Affiche '((0.00,0.00),)'
for i in range(3):
    turtle.forward(90)
    turtle.left(90)
turtle.end_poly()  #Finit d'enregistrer
print(turtle.get_poly())  #Affiche '((0.00,0.00), (90.00,0.00), (90.00,90.00), (0.00,90.00))'

En définitive, cela va nous être très utile, car nous allons pouvoir savoir quel est le point de départ et tester si le point d’arrivée est identique à ce dernier dans le but savoir s’il faut que nous fassions la jonction ou non. En utilisant ce que l’on vient de dire, voici ce que nous pouvons coder, suivi de quelques explications :

Le code :

import turtle

EPSILON = 10**-10

def notre_begin_fill():
    """Fonction pour commencer à remplir"""
    turtle.begin_fill()
    turtle.begin_poly()

def notre_end_fill():
    """Fonction pour finir de remplir"""
    turtle.end_poly()
    poly = turtle.get_poly()
    #On teste pour savoir si le point actuel est égal à celui de départ
    if abs(poly[0][0]-poly[-1][0]) > EPSILON or abs(poly[0][1]-poly[-1][1]) > EPSILON:
        turtle.goto(poly[0])
    turtle.end_fill()
    
if __name__ == "__main__":
    turtle.color("red", "green")
    notre_begin_fill()
    for i in range(4):
        turtle.forward(200)
        turtle.left(90)
    notre_end_fill()

La fonction notre_begin_fill nous permet tout simplement de commencer à remplir ainsi qu’à enregistrer les points par lesquels nous passons. Ensuite, à travers notre_end_fill, nous terminons l’enregistrement, nous testons si le point actuel correspond au point d’arrivée afin de tracer le dernier trait si besoin et nous pouvons enfin terminer le remplissage.

Pour comparer nos deux points, nous devons être précautionneux. En effet, la comparaison directe de deux nombres flottants est déconseillée, car elle peut aboutir à des erreurs difficiles à cerner. Si besoin, vous pourrez en apprendre plus à travers les réponses de ce sujet.

C’est le moment de passer à la pratique !

TP : Atelier coloriage

Comme pour les travaux pratiques de la partie précédente, le but sera de coder le programme permettant d’arriver à l’image ci-dessous (encore une fois, ne vous préoccupez pas des longueurs). Veillez à colorier du plus gros au plus petit afin que le petit ne soit recouvert par le gros à chaque fois.

Résultat à reproduire.
Résultat à reproduire.

La correction :

import turtle

def deplacer_sans_tracer(x, y = None):
    """Fonction pour se déplacer à un point sans tracer"""
    turtle.up()
    if (isinstance(x, tuple) or isinstance(x, list)) and len(x) == 2:
        turtle.goto(x)
    else:
        turtle.goto(x, y)
    turtle.down()
        
def carre(longueur):
    """Fonction pour tracer un carré depuis le coin bas gauche"""
    for nb_cote in range(4):
        turtle.forward(longueur)
        turtle.left(90)

if __name__ == "__main__":
    turtle.bgcolor("orange")
    #Le triangle
    deplacer_sans_tracer(-210, -210)
    turtle.color("white", "pink")  #pencolor("white"); fillcolor("pink")
    turtle.begin_fill()
    turtle.goto(210, -210)
    turtle.goto(0, 240)
    turtle.goto(-210, -210)
    turtle.end_fill()
    #Le cercle
    deplacer_sans_tracer(0, 0)
    rayon = turtle.distance(135, 135)
    deplacer_sans_tracer(0, -rayon)
    turtle.fillcolor("purple")
    turtle.begin_fill()
    turtle.circle(rayon)
    turtle.end_fill()
    #Les carrés
    carres = [("green", 270), ("yellow", 180), ("grey", 90)]
    for var_carre in carres:
        deplacer_sans_tracer(-var_carre[1]/2,-var_carre[1]/2)
        turtle.color("black", var_carre[0])  #pencolor("black"); fillcolor(var_carre[0])
        turtle.begin_fill()
        carre(var_carre[1])
        turtle.end_fill()
    deplacer_sans_tracer(0, 0)
    turtle.exitonclick()

Si vous souhaitez vous exercer davantage avec le coloriage, vous pouvez, par exemple, ajouter des couleurs au ciel étoilé que nous avons fait dans la partie précédente, ou vous pouvez aussi essayer de dessiner une voiture, une maison ou encore un robot.


Voilà, vous en savez désormais un peu plus sur turtle. Après la configuration et le dessin, nous allons voir comment interagir avec l’utilisateur, en lui demandant de saisir une valeur par exemple !