Catégorie : Art

Art

Balles et ballons : les ballons du système solaire

Le premier projet de la classe de 1ère NSI est « Regard de géomètre ». Ce projet consiste en la création d’une image basée sur un ou plusieurs thèmes donnés. Pour notre image, on a choisi les thèmes astronomie et balles et ballons. Nous avons voulu représenter le système solaire mais en remplaçant les planètes et le Soleil par des balles et des ballons de différents sports.

Les étapes de création de notre image

Après avoir choisi nos thèmes et décidé de l’illustration que l’on souhaitait faire, nous avons commencé par le fond de notre illustration. Pour cela, on voulait un fond noir avec des étoiles représentées par des points blancs et de tailles différentes placées aléatoirement sur l’image. Cependant, les grosses étoiles à cause de leur couleur blanche, se voyaient trop par rapport aux plus petites. On a alors ajouté une couleur un peu plus grise pour les grosses étoiles mais on a gardé le blanc pour les petites. Pour faire cela, nous avons utilisé une instruction conditionnelle :

import turtle
import random
turtle.speed(0)
for i in range(150):
    turtle.penup()
  turtle.goto(random.randint(-640,640),random.randint(-350,350)) 
    turtle.pencolor("black")
    turtle.pendown()
    a=random.randint(1,5)
    if a<2:
        turtle.begin_fill()
        turtle.fillcolor("white")
        turtle.circle(a)
        turtle.end_fill()
    else:
        turtle.begin_fill()
        turtle.fillcolor("#e1e1df")
        turtle.circle(a)
        turtle.end_fill()

Ce qui donne avec le fond:

Tracé du ciel étoilé

Dans un second temps, nous avons choisi les balles que l’on voulait représenter puis nous les avons tracées chacune séparément pour avoir le rendu le plus fidèle possible. Les balles choisies sont:

  • la balle de tennis
  • le ballon de basket-ball
  • le ballon de rugby
  • la balle de base-ball

A l’origine, on voulait faire le ballon de football plutôt que la balle de base-ball mais le motif de ce ballon est un peu trop complexe à faire et n’apportait que peu d’intérêt pour notre script.

Script pour le tracé du ballon de basket :

import turtle
turtle.bgcolor("black")
turtle.begin_fill()
turtle.fillcolor("coral")
turtle.goto(0,-3)
turtle.circle(98)
turtle.end_fill()
turtle.penup()
turtle.goto(-97,93)
turtle.left(-73)
turtle.pendown()
turtle.pensize(5)
turtle.pencolor("black")
turtle.circle(100,150)
turtle.penup()
turtle.goto(-97,93)
turtle.left(180)
turtle.pendown()
turtle.circle(100,-150)
turtle.penup()
turtle.goto(-97,93)
turtle.right(107)
turtle.pendown()
turtle.forward(186)

Tracé du ballon de basket

Dans la dernière étape de la création de notre image, nous avons regroupé toutes nos créations (fond, balles et ballons) dans un même script et nous avons rajouté certaines fonctionnalités comme les ellipses ou l’effet d’ombre qui vous seront montrés plus loin. Mais, lors de ce regroupement, nous avons fait face à plusieurs problèmes notamment au positionnement des balles et également à leur proportion (un ballon de basket ne peut pas être plus petit qu’une balle de tennis). Pour cela, on a effectué une suite d’essai-erreur jusqu’à ce que le résultat nous plaise.

Rendu final

Les fonctions utilisées dans le script

La première fonction importante de ce script est la fonction « orbite ». Elle consiste en la création d’une « fausse ellipse » à l’aide de 4 arcs de cercles de rayons différents. Cette fonction est plutôt simple mais extrêmement efficace.

def orbite(rayon):
  turtle.seth(-45) 
  for i in range(2):
      turtle.circle(rayon,90)
      turtle.circle(rayon/2,90)
  return()

La deuxième fonction marquante de ce script est la fonction permettant de créer un effet d’ombre sur les balles et ballons ayant une forme circulaire. Pour cela, il faut créer un dégradé de couleur formé à l’aide de cercles de plus en plus petits dont la couleur varie en même temps que le rayon diminue. Cette fonction s’appelle « surface ».

def surface(couleur,rayon,posx,posy):
    r=couleur[0]
    g=couleur[1]
    b=couleur[2]
    turtle.pensize(5)
    for i in range(51):
        turtle.home()
        turtle.goto(posx,posy)
        turtle.pendown()
        if ((r-i*1.5)>=0) and ((g-i*1.5)>=0) and ((b-i*1.5)>=0):
            r1=r-i*1.5; g1=g-i*1.5; b1=b-i*1.5;
        elif ((r-i*1.5)<0): r1=r
        elif ((g-i*1.5)<0): g1=g
        else: b1=b
        turtle.pencolor((r1/255,g1/255,b1/255))
        turtle.circle((100-2*i)*rayon)
        turtle.penup()

Enfin, la dernière fonction est la fonction étoile, elle permet comme son nom l’indique de tracer des étoiles de taille et avec un nombre de branches variables. Elle a pour paramètres le nombre de branches et la longueur des branches.

def etoiles(branches, longueur):
    turtle.begin_fill()
    turtle.fillcolor("white")
    for i in range(branches):
        turtle.forward(longueur)
        turtle.right(180+180/branches)
        turtle.forward(longueur)
        turtle.left(180-180/branches)
    turtle.end_fill()

Malheureusement, cette fonction a un défaut que l’on n’a pas su régler. Lorsque l’on choisit un nombre pair de branches, le nombre n’est pas respecté.

Exemple: lorsque l’on exécute etoiles(6,100). Le nombre de branches n’est pas correct.

Alors que lorsqu’on veut faire une étoile avec un nombre de branches impair, le nombre de branches est respecté.

Exemple: lorsque l’on exécute etoiles(9,100). Le nombre de branches est correct.

Problèmes rencontrés

Lors de la création de notre script, nous avons rencontré plusieurs problèmes comme le positionnement des balles ou encore la proportion des ballons mais également des problèmes qui survenaient lorsqu’on voulait créer une image. Ce problème venait de la commande bgcolor(). Cette fonction consiste à colorier le fond de la toile par la couleur de son choix. Cependant, sur le script nous permettant de créer nos images, le fond n’apparaissait pas. Pour résoudre ce problème, nous avons rajouté au script une boucle faisant des allers-retours sur la toile à l’aide d’un stylo épais permettant de créer manuellement ce fond.

turtle.goto(-760,400)
turtle.pensize(10)
turtle.right(90)
for i in range(75):
    turtle.forward(420*2)
    turtle.left(90)
    turtle.forward(10)
    turtle.left(90)
    turtle.forward(420*2)
    turtle.right(90)
    turtle.forward(10)
    turtle.right(90)

Conclusion sur notre premier projet en NSI

Ce premier projet nous a permis d’approfondir nos connaissances en turtle et de créer notre premier « gros script » de cette année en python en faisant appel à toutes les fonctions vues en cours. Nous aurions pu essayer d’apporter des améliorations avec un peu plus de temps en ajoutant par exemple d’autres balles plus complexes comme la balle de golf.

Télécharger le .py

L’image finale

Art

Jardins : Forêt enneigée

Ce script génère une image aléatoire de forêt enneigée en utilisant deux fractales très connues : l’Arbre de Pythagore et le Flocon de Koch.

Les fractales

Le Flocon de Koch

Le Flocon de Koch utilise une courbe fractale. Une courbe fractale est un dessin composé de segment dont la forme de base va être reproduite à l’infini.

Dans le cas de la courbe de Koch, sur chaque segment, un triangle va être dessiné, et pour chaque niveau, on va rajouter un triangle sur les nouveaux segments.

Pour créer cette courbe, on va utilisé le principe de récursivité, c’est à dire qu’on va appeler dans une fonction python la fonction elle-même.

def cote(longueur,niveau):
    if niveau==0:
        forward(longueur)
    else:
        cote(longueur/3,niveau-1)
        left(60)
        cote(longueur/3,niveau-1)
        right(120)
        cote(longueur/3,niveau-1)
        left(60)
        cote(longueur/3,niveau-1)

On exécute la fonction cotée en définissant la longueur et le niveau de répétition souhaité. Ligne 2, on vérifie que le niveau est égal à 0. S’il ne l’est pas, ligne 5, on rappelle la fonction et cette fois-ci la longueur est divisée par 3 et on enlève 1 au niveau. Ainsi, on répète tant que le niveau n’est pas égal à 0, et quand le niveau est égale à 0, on avance de « longueur ».

En répétant cela plusieurs fois, on obtient la courbe de Koch.

Pour obtenir un flocon, on répète cette courbe trois fois.

def flocon(longueur,niveau,x,y):
    width(1)
    penup()
    goto(x,y)
    pendown()
    for i in range(3):
        cote(longueur,niveau)
        right(120)

Ce code provient de https://www.numworks.com/fr/professeurs/activites-pedagogiques/premiere/flocon/

L’arbre de Pythagore

L’arbre de Pythagore fonctionne de la même manière : la fonction s’appelle elle-même.

def arbre(n,longueur,w,r1,g1,b1,r2,g2,b2):
  if n==0 :
    pencolor(r1,g1,b1)
    forward(longueur)
    backward(longueur)
    pencolor(r2,g2,b2)
  else:
    width(w) 
    forward(longueur/3)
    left(50) 
    arbre(n-1,longueur*2/3,w/1.2,r1,g1,b1,r2,g2,b2)
    right(2*50)
    arbre(n-1,longueur*2/3,w/1.2,r1,g1,b1,r2,g2,b2)
    left(50)
    backward(longueur/3)

Cette fonction prend plusieurs paramètres :

  • le niveau n de répétition
  • la longueur du tronc
  • la largeur w : afin que les arbres éloignés soient plus fins que les arbres au premier plan, on définit la largeur. Plutôt que d’utiliser w-1 (sinon après un certain niveau de répétition la valeur deviendrait négative), on utilise w/1.2 ce qui permet de réduire la valeur sans changer son signe.
  • r1,g1,b1 : qui définissent en RGB la couleur des feuilles.
  • r2,g2,b2 : qui définissent en RGB la couleur du tronc.

Ce code provient de https://my.numworks.com/python/cent20/arbre

Le fond

L’exécution du fond et des montagnes

A chaque exécution le fond va être d’une couleur aléatoire.

color(randint(10,250),255,255)
begin_fill()
goto(-640,700)
pendown()
goto(640,700)
goto(640,-700)
goto(-640,-700)
end_fill()

A chaque fois que nous exécutons le script, le résultat est unique, les montagnes ayant une forme définie auront une couleur aléatoire.

Script d’une des montagnes :

for i in range (10):
    for i in range (5):
        forward(20)
        right(2)
    for i in range (5):
        forward(10)
        left(2)

Cette boucle crée des fragments de cercles collés, qui forment une ondulation.

Les trois montagnes donnent cette image, avec des couleurs uniques à chaque fois.

L’exécution des arbres

On dessine 7 arbres en tout : 2 sur la montagne du fond, 2 sur la montagne du milieu et trois sur la montagne du devant. Chaque arbre a un secteur donné sur sa montagne et il est dessiné dans ce secteur, de façon unique à chaque fois.

Les arbres sur la montagne la plus éloignée sont plus petits, plus fins et plus sombres, cependant chaque arbre est d’une couleur, d’une taille et d’une largeur différentes à chaque exécution.

Voici par exemple l’exécution d’un arbre sur la montagne du fond

penup()
goto(randint(-550,-450),randint(120,160))
pendown()
setheading(90)
color(75,randint(40,50),randint(0,20))
arbre(9,randint(50,90),randint(4,7),randint(130,180),randint(160,180),180,75,randint(40,50),randint(0,20))

L’exécution des flocons

Ensuite on exécute les flocons:

for i in range (randint(30,45)):
    setheading(randint(0,360))
    color(randint(0, 220),randint(110, 240),255)
    begin_fill()
    flocon(randint(20,50),randint(3,5),randint(-639,639),randint(-310,310))
    end_fill()

A chaque exécution, le nombre de flocons est différent. Chaque flocon est orienté différemment , à une couleur, une taille, un niveau de répétition, et un emplacement différent.

L’image finale

Télécharger le .py

Si vous voulez l’essayer, vous trouverez le script ci-dessous, cependant faites attention, chaque arbre prend relativement longtemps à s’exécuter, ajouté aux flocons, le script prend environ 1h30 à s’exécuter.

Art

Pendules : Une horloge analogique

Une horloge analogique… Qu’est-ce cela peut bien être… Eh bien, c’est tout simplement une horloge avec des aiguilles ! Ainsi, ce programme vous permettra, grâce à Turtle, d’afficher une horloge analogique…

Introduction

La spécificité de cette horloge, est que celle-ci est dynamique. C’est-à-dire que les aiguilles bougent en fonction de l’heure réel. Sinon cela serait trop facile, il suffirait seulement de dessiner un cercle pour le cadran, et des segments pour les aiguilles, et c’est fini. Heureusement, ce n’est pas le cas.

Quelques fonctions

Pour afficher l’heure réel, il faut d’abord récupérer l’heure réel… Pour cela, j’utilise la fonction time.strftime(), à partir du module time, je récupère une donnée de la date actuelle, en l’occurrence j’ai besoin de 3 données : l’heure, la minute, et la seconde. Voici donc une fonction que j’ai codé qui m’a permis de récupérer une de ces 3 données :

def get_time(u):
    match u:
        case "h":
            return int(strftime("%I"))
        case "m":
            return int(strftime("%M"))
        case "s":
            return int(strftime("%S"))

Ici, j’ai utilisé la déclaration match, qui permet, entre autres, de comparer la valeur d’une variable, ici la variable u (pour unité). Par exemple, en appelant la fonction suivante get_time("h"), elle me renvoi la valeur de l’heure actuelle, à l’heure où j’écris cet article, il est exactement 01:44 donc en appelant la fonction maintenant, elle me renverrait la valeur 1.
À savoir qu’utiliser la déclaration match n’est absolument pas obligatoire, j’aurais très bien pu utiliser une structure conditionnelle traditionnelle avec les déclarations if, elif, et else.

Désormais, j’ai besoin d’afficher le cadran de l’horloge, pour cela, rien de plus simple :

def draw_clock(x, y, rayon):
    travel(x, y-rayon)
    pencolor((0,)*3)
    fillcolor((255,)*3)
    begin_fill()
    circle(rayon)
    end_fill()
    travel(x, y-rayon+10)
    fillcolor(7, 24, 31)
    begin_fill()
    circle(rayon-10)
    end_fill()
    travel(x, y-5)
    fillcolor(85, 91, 92)
    begin_fill()
    circle(5)
    end_fill()
    penup()
    goto(x, y)
    setheading(90)
    pencolor((255,)*3)
    for i in range(60):
        forward(rayon-(25 if i%5 == 0 else 15))
        pendown()
        forward(25 if i%5 == 0 else 15)
        penup()
        goto(x, y)
        right(6)

C’est une fonction plutôt simple, bien qu’elle soit assez lourde. On dessine des cercles, et on fait les marquages des minutes et des heures. J’ai dans cette fonction, utilisé une autre fonction qui ne vient pas des modules importés, la fonction travel() (pour voyager). J’aime beaucoup cette fonction car elle est extrêmement simple, et permet d’économiser quelques lignes :

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

En fait, c’est tout simplement l’équivalent d’un goto(), mais sans laisser les traces du pinceau ! Voici donc le cadran, il est fixe :

J’ai également voulu ajouter dans le fond un mur en briques. Pour ne pas laisser le fond vide, la fonction fait seulement 7 lignes :

def wall_brick(x, y, width_bricks, height_bricks):
    for i in range(height_bricks):
        for j in range(width_bricks):
            if i % 2 == 0:
                fill_rect(x+65*j, y+35*i, 60, 30, (161, 84, 68))
            else:
                fill_rect(x+30+65*j, y+35*i, 60, 30, (161, 84, 68))

Les paramètres sont simples, la position du mur (le bas à gauche du mur) via x et y, et le nombre de briques en longueurs, ainsi que le nombre de briques en hauteur. fill_rect() est une fonction maison inspiré de kandinsky.fill_rect(), elle permet de dessiner des rectangles :

def fill_rect(x, y, w, h, c=(0, 0, 0)):
    travel(x, y)
    pencolor(c)
    fillcolor(c)
    begin_fill()
    for i in range(4):
        forward(w if i % 2 == 0 else h)
        right(90)
    end_fill()

Ci-dessous donc, le mur en briques, contruit par la fonction wall_brick(-668, -330, 21, 21) :

Difficultés rencontrées

Il ne reste donc plus qu’à dessiner les aiguilles et les faire pivoter au fur et à mesure. Voici donc la fonction qui occupe cette lourde tâche, et qui m’a bien cassé la tête :

def clock_hands(colors_hands, size_hands):
    s, m, h, k = get_time("s"), get_time("m"), get_time("h"), 0
    update_hours(h, colors_hands[0], size_hands[0])
    update_minutes(m, colors_hands[1], size_hands[1])
    while True:
        update_seconds(s, colors_hands[2], size_hands[2])
        sleep(.6 if k < 90 else .5 if k < 180 else .3 if k < 240 else 0)
        s += 1
        if s == m+2:
            update_minutes(m, colors_hands[1], size_hands[1])
        if s+1 == (h+1)*5:
            update_hours(h, colors_hands[0], size_hands[0])
        if s == 60:
            s, m, h = get_time("s"), get_time("m"), get_time("h")
            update_minutes(m, colors_hands[1], size_hands[1])
            update_hours(h, colors_hands[0], size_hands[0])
            update_seconds(s, colors_hands[2], size_hands[2], 1)
        k += 1

C’est sans doute la fonction la plus compliquée du programme. Les deux paramètres ne sont qu’esthétique, ils permettent de modifier la couleur de chaque aiguille, ainsi que la largeur de chaque aiguille, on ne donc y s’attarder plus que ça.
L’objectif est simple, avoir l’heure la plus précise possible, mais en gardant un mouvement des aiguilles fluide.
J’ai fait le choix d’appeler la fonction get_time() le moins de fois possible, car elle requiert de faire un nombre extrêmement élevé de requêtes plusieurs fois par secondes. Et faire des requêtes inutiles, ça ralentit le programme… La fonction n’est donc appelée que lors de l’initialisation (avant de rentrer dans la boucle infinie), et lorsque la trotteuse à passe à la soixantième seconde. Nous appelons donc la fonction get_time() environ une fois par minute ; et c’est suffisant. Ensuite, on met à jour la position des aiguilles toutes les secondes… Enfin, c’est un peu plus compliqué que ça. En effet, il faut que le mouvement soit fluide, et donc il faut supprimer toutes les actions inutiles. Sauf que 98% du temps, il n’y a que l’aiguille des secondes qui bouge, celle des minutes et des heures ne bougent beaucoup moins souvent. Donc cela ne sert à rien de mettre à jour ces aiguilles à chaque seconde. On le fait donc à des instants précis.
Malgré toutes les optimisations pour fluidifier le rafraichissement, il n’est malheureusement pas parfait.
Également, il s’avère que plus le programme tourne longtemps, plus il est lent… Il faut donc adapter la pause de la fréquence en fonction de quand le programme a démarré. Voici à quoi sert la variable k, et la fonction time.sleep(), nous perdons donc beaucoup de précisions au fil du temps. Mais en contrepartie, j’ai mis en place un système pour rerégler automatiquement les aiguilles.

L’image finale

À toi de « jouer » ?!

Tu ne pourras malheureusement pas vraiment t’amuser avec ce programme. À moins que tu veuilles fixer une horloge. Néanmoins, si tu veux savoir comment elle fonctionne exactement, et la voir bouger, n’hésite pas à télécharger le script !

Télécharger le .py