Bill Cipher en Python

Projets

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

def cercle_centre(x):
    penup()
    goto(0, -x)
    pendown()
    circle(x)

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.

cercle_centre(450)
cercle_centre(440)
cercle_centre(370)
cercle_centre(360)

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.

bill(1)

Image finale

Télécharger le fichier python