Blog

Page d’exemple

Art

Perspective : un paysage Synthwave

Dans le cadre de la 5ème édition du dispositif “Regards de Géomètre”, nous avons décidé dans le thème « Perspective » de produire un paysage synthwave.

Vidéo du projet

Origines de la Synthwave

Avant tout la synthwave est un genre musical électronique ayant émergé dans les années 2000/2010 qui s’inspire d’éléments des années 80. Et c’est justement dans les clips de ces musiques que l’on va retrouver ce type d’image.

Pour en savoir plus nous vous conseillons cette vidéo qui explique brièvement les origines.

Le Projet

Revenons donc au projet. Pour ce projet nous avons donc décidé de produire une image d’un paysage synthwave. Pour cela nous utilisons le module turtle ainsi que le module random, le module turtle est utilisé pour produire l’image est le module random est utilisé pour les étoiles de l’image où la taille et leur position est générée aléatoirement nous avons également utilisé le script permettant d’exporter une image générée par turtle en .png que vous pouvez retrouver ici.

Structure du script

Pour la structure du script nous avons décidé de découper chaque partie de l’image (pavage, fond, étoiles, soleil, ville et montagnes) afin de créer des fonctions et à la fin nous les avons toutes appelées dans un ordre précis pour que les différentes parties de l’image soit dans leur plan respectif.

Analyse du script

Nous allons donc analyser le script.

Commençons par l’appel des modules et la mise en place de turtle.

from turtle import *
from random import randint
# vérification des modules importés
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 - Un paysage Synthwave"
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")
setup(1280, 720) # définit la taille de la fenêtre
colormode(255) # permet l'utilisation de couleurs rgb
speed(0) #Remplaçable par tracer(2) (10x plus rapide) mais si il est utilisé des lignes du pavage peuvent manquer
hideturtle() #dissimule la tortue

On appelle les fonctions turtle, random avec pour random uniquement randint et on utilise une partie du script « exporter une image générée par turtle » pour 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 la vérification on met en place le titre de la fenêtre qui va affiché le rendu ainsi que sa taille. Enfin on définit le type de couleurs utilisées (R,G,B), la vitesse de la tortue, et on dissimule la tortue (c’est plus joli).

Commençons par la première fonction : le fond

def fond():
    penup()
    rciel = 0
    gciel = 0 
    bciel = 0
    hauteur = -360
    goto(-642,-358)
    pendown()
    while hauteur != 360:
        pencolor(round(239 + rciel), round(41 + gciel), round(209 + bciel))
        forward(1280)
        hauteur += 1
        goto(-640, hauteur)
        rciel += (-29/180)
        gciel += (2/45)
        bciel += (7/720)

Pour le fond on aurait pu utiliser une fonction qui crée un rectangle et qui le remplit avec fill_rect, cependant la couleur dans ce cas est uni ce qui ne nous intéresse pas. Nous avons donc produit un script qui fait un fond dégradé qui fait avancer la tortue sur une ligne d’un pixel de large et à la fin de cette ligne la tortue est envoyé grâce à un goto à la ligne d’après 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. Tout ceci est arrondi car turtle n’est pas compatible avec des arguments à virgule (pour la fonction pencolor en tout cas).

Par la suite la fonction qui produit les étoiles a été codée :

def etoile():
    for i in range(90):
        penup()
        goto(randint(-720,720), randint(0,360))
        pendown()
        pencolor(255, 255, 255)
        lcercle = randint(1,3)
        fillcolor('white')
        begin_fill()
        circle(lcercle)
        end_fill()

Pour les étoiles on définit aléatoirement leur position sur la moitié haute de l’image, on les met en blanc, on définit aussi aléatoirement la taille de l’étoile et on créer l’étoile avec sa position, et sa taille en aléatoire puis on refait ce processus 90 fois pour avoir 90 étoiles.

Ensuite nous avons le soleil (Le script affiché n’est qu’une petite partie du script total du soleil car il est très long et qu’il se répète, il est donc inutile de commenter la suite) :

def soleil():
    penup()
    liste1 = [10,7,5,4,3,3,3,3,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,1,2,1,1,1]
    liste2 = [1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0]
    pliste1 = 0
    pliste2 = 0
    rsoleil = 0
    gsoleil = 0
    bsoleil = 0
    lsoleil = 8
    hauteur = 30
    goto(0,30)
    pendown()
    for i in range(15):
        pencolor(round(255 + rsoleil), round(23 + gsoleil), round(226 + bsoleil))
        forward(lsoleil)
        backward(2*lsoleil)
        hauteur += 1
        lsoleil += liste1[pliste1]
        pliste1 += 1
        goto(0, hauteur)
        rsoleil += (0)
        gsoleil += (114/101)
        bsoleil += (-203/202)
    forward(lsoleil)
    backward(2*lsoleil)
    penup()

Pour le soleil nous réutilisons le script du dégradé, cependant nous l’avons modifier pour que le point d’origine de la tortue soit au centre du rectangle et que la longueur de chaque ligne soit défini par une liste (ce qui permet de faire un cercle en prenant les longueur d’un cercle utilisable en pixel art). Pour ce qui est des endroits ou le soleil est coupé la tortue fait le tracé mais on a utilisé la fonction penup() qui fait que la tortue ne dessine plus.

Après nous avons la fonctions des montagnes (Encore une fois le script n’est pas complet car il se répète encore 3 fois après.) :

def montagne():
    #montagne derrière la première
    penup()
    goto(-480,0)
    fillcolor(110, 27, 188)
    begin_fill()
    for i in range(3):
        forward(250)
        left(120)
    end_fill()
    
    goto(-480,0)
    pencolor(51, 210, 246)
    ymontagne = 10
    for i in range(11):
        pendown()
        goto(-355,ymontagne)
        goto(-230,0)
        penup()
        goto(-480,0)
        ymontagne += 20

Pour le script des montagnes nous avons utilisé le script pour faire des triangles équilatéraux retrouvable ici. Et ensuite nous avons utilisé un ‘for i in range’ pour faire des goto enchaîné pour faire les lignes des montagnes.

Ensuite nous avons les fonctions de la ville. Pourquoi les fonctions car il y a une fonction pour chaque bâtiment différent ainsi qu’une fonction finale qui définit l’ordre des bâtiments.

def bat1():
    penup()
    rbat = 0
    gbat = 0 
    bbat = 0
    hauteur = 0
    pendown()
    xturtle, yturtle = pos()
    while hauteur != 72:
        pencolor(round(125 + rbat), round(35 + gbat), round(216 + bbat))
        forward(42)
        hauteur += 1
        goto(xturtle, hauteur)
        rbat += (-5/3)
        gbat += (-7/15)
        bbat += (-72/25)
    forward(42)
    penup()
    right(90)
    forward(72)
    left(90)

Pour le bâtiment 1 il y a un dégradé (toujours le même script) puis on fait que la tortue finisse le bâtiment en bas à droite de ce dernier pour pouvoir enchainer les bâtiments.

fun fact : Au début nous n’avions pas prévu les quelques lignes à la fin pour que la tortue puisse enchaîner les bâtiments sans que les fonctions des bâtiments aient besoin d’être modifiés ce qui nous a amené à avoir tous les bâtiments qui se chevauchaient.

def ville():
    penup()
    goto(-320,0)
    bat3(), bat2(), bat1(), bat4() ,bat3(), bat4(), bat3(), bat2(), bat1(), bat2(), bat1(), bat3(), bat1(), bat4(), bat2(), bat1(), bat3(), bat1(), bat4(), bat3()

Et à la fin on a fait la fonction ville qui appelle dans l’ordre choisi les bâtiments. Le goto permet de définir où commence le premier bâtiment, les autres se mettent juste après le premier sans se chevaucher ni laisser un espace.

Par la suite nous avons le pavage (très long aussi, il sera donc coupé) :

def pavage():
    colormode(255)
    pensize(5)
    speed(0)
    rciel = 0
    gciel = 0 
    bciel = 0
    hauteur = -360
    penup()
    goto(-640,-360)
    pendown()
    while hauteur != 0:
        pencolor(round(15 + rciel), round(4 + gciel), round(76 + bciel))
        forward(1280)
        hauteur += 1
        goto(-640, hauteur)
        rciel += (91/180)
        gciel += (1/36)
        bciel += (7/18)

Pour le début du pavage on retrouve encore le script du dégradé mais avec les couleurs modifiées.

pencolor(229, 123, 240)
    #Lignes au dessus du pavage
    pensize(4),penup(),goto(-640,0),pendown(),goto(640,0),pensize(2),penup(),goto(-640, 0),pendown()
    #lignes gauche
    penup(),goto(-20.00,0),pendown(),goto(-60.00,-360.00),penup(),goto(-60.00,0),pendown(),goto(-180.00,-360.00),penup(),goto(-100.00,0),pendown(),goto(-300.00,-360.00),penup(),goto(-140.00,0),pendown(),goto(-420.00,-360.00),penup(),goto(-180.00,0),pendown(),goto(-540.00,-360.00),penup(),goto(-220.00,0),pendown(),goto(-660.00,-360.00),penup(),goto(-260.00,0),pendown(),goto(-780.00,-360.00),penup(),goto(-300.00,0),pendown(),goto(-900.00,-360.00),penup(),goto(-340.00,0),pendown(),goto(-1020.00,-360.00),penup(),goto(-380.00,0),pendown(),goto(-1140.00,-360.00),penup(),goto(-420.00,0),pendown(),goto(-1260.00,-360.00),penup(),goto(-460.00,0),pendown(),goto(-1380.00,-360.00),penup(),goto(-500.00,0),pendown(),goto(-1500.00,-360.00),penup(),goto(-540.00,0),pendown(),goto(-1620.00,-360.00),penup(),goto(-580.00,0),pendown(),goto(-1740.00,-360.00),penup(),goto(-620.00,0),pendown(),goto(-1760.00,-360.00)
    #lignes droites
    penup(),goto(20,0),pendown(),goto(60.00,-360.00),penup(),goto(60.00,0),pendown(),goto(180.00,-360.00),penup(),goto(100.00,0),pendown(),goto(300.00,-360.00),penup(),goto(140.00,0),pendown(),goto(420.00,-360.00),penup(),goto(180.00,0),pendown(),goto(540.00,-360.00),penup(),goto(220.00,0),pendown(),goto(660.00,-360.00),penup(),goto(260.00,0),pendown(),goto(780.00,-360.00),penup(),goto(300.00,0),pendown(),goto(900.00,-360.00),penup(),goto(340.00,0),pendown(),goto(1020.00,-360.00),penup(),goto(380.00,0),pendown(),goto(1140.00,-360.00),penup(),goto(420.00,0),pendown(),goto(1260.00,-360.00),penup(),goto(460.00,0),pendown(),goto(1380.00,-360.00),penup(),goto(500.00,0),pendown(),goto(1500.00,-360.00),penup(),goto(540.00,0),pendown(),goto(1620.00,-360.00),penup(),goto(580.00,0),pendown(),goto(1740.00,-360.00),penup(),goto(620.00,0),pendown(),goto(1760.00,-360.00)
    #Lignes horizontales
    penup(),goto(-640, -300),pendown(),goto(640, -300),penup(),goto(-640, -240),pendown(),goto(640, -240),penup(),goto(-640, -190),pendown(),goto(640, -190),penup(),goto(-640, -140),pendown(),goto(640, -140),penup(),goto(-640, -100),pendown(),goto(640, -100),penup(),goto(-640, -70),pendown(),goto(640, -70),penup(),goto(-640, -40),pendown(),goto(640, -40),penup(),goto(-640, -15),pendown(),goto(640, -15),

On a par la suite énormément de goto afin de faire le quadrillage du pavage.

Pour produire l’image finale nous avons les appels des différents fonctions à la fin :

#appel de toutes les fonctions
fond(), etoile(), soleil(), montagne(), ville(), pavage()

Et pour exporter l’image finale en .png il y a la suite du script pour exporter une image générée par turtle en .png utilisé au début du script.

#enregistrement de l'image finale avec vérification des modules importés
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 script va donc générer une image en .ps et la convertir en .png avec un nom généré aléatoirement pour éviter que à chaque fois que vous générez une image l’image soit écrasée

Télécharger le .py

L’image finale

Projets

Un météorologue personnel en briquette !

Qui n’a jamais rêvé d’avoir un robot en LEGO qui lui annonce la météo ? Et bien même si ce n’est pas le cas, nous l’avons fait pour vous ! Venez découvrir un robot capable de comprendre son environnement pour vous donner la météo.

Fonctionnalités du robot

Notre robot au doux nom de ROBKEY est capable de :

  • déplacer ses bras et son bassin ;
  • reconnaitre l’endroit où il se trouve pour donner précisément la météo ;
  • afficher l’heure, la météo et la température ambiante ;
  • d’avoir quelques réactions faciales ;
  • et bien plus.

La conception du robot

Pour concevoir notre robot, nous avons dû prendre en compte plusieurs choses. Tout d’abord, les moteurs n’ont pas été particulièrement dur à installer car il ne font que la moitié de la taille de l’esp 32. Il nous a donc paru évident de mettre les moteurs des bras dans le torse du robot et le moteur du bassin dans le châssis de ses chenilles.

Ensuite il a fallu installer planche de test qui a eu plusieurs positions possibles. Premièrement dans son dos car cela aurait permis de pouvoir faire tourner le bassin du robot à 360°, mais les câbles auraient difficilement bien tenu à l’esp 32 et au robot lui-même. Nous nous sommes finalement entendu à l’idée de mettre la planche sur la batterie des chenilles bien que cela empêche le robot de faire des tours sur lui même.

Maintenant, le plus important : le placement de l’ESP32. Nous avions deux idées, l’une le plaçait dans le torse du robot, l’autre faisait en sorte qu’il soit la tête légèrement rentrée dans les épaules. Nous avons opté pour la seconde option car celle-ci nous permet d’afficher un petit visage sur l’écran du microcontrôleur.

Pour conceptualiser l’apparence de notre robot, nous avons donc réalisé un schéma en 3D très simple sur Paint 3D en regroupant nos idées pour imaginer ce à quoi il ressemblera.

Construction de la structure du robot

Pour construire le robot, j’ai (Sylvain) commencé par regarder les constructions Lego que j’avais précédemment réalisées et qui pouvaient être détruite pour commencer à récupérer leurs pièces. Avec toutes ses matières premières je me suis laissé porter par mon imagination pour construire le LEGO le plus ressemblant à un robot en essayant de faire en sorte qu’il soit plutôt résistant (bon d’accord j’ai peut-être failli à cette tâche).

C’est grâce aux joies de la maladie que j’ai pu entamer la construction. Pour commencer, j’ai récupéré le châssis d’une autochenille radiocommandée en la modifiant pour la rendre plus compacte et en laissant un espace pour le moteur du bassin.

Ensuite j’ai commencé à faire ledit bassin, que j’élargis à la fin de la construction car je l’avais fait trop fin. Suivi du torse, le plus simple car il devait être creux pour laisser la places aux câbles et aux moteurs. Il fallait aussi préparer les trous pour les axes des bras.

Après j’ai fait les bras qui sont vraiment très simplistes mais à ce moment je commençais sérieusement à manquer de pièces plus techniques et même des plus simples pour faire quelque chose de plus sympathique. Mais quoi qu’il en est l’un des deux bras représente un soleil pour quand il fait beau, tandis que l’autre représente un ciel nuageux.

Pour finir j’ai réalisé la cage de la planche de test et solidifié les éléments les moins solides et résolu les quelques problèmes mécaniques rencontrés lors des premiers tests des moteurs.

L’électronique du robot

En parallèle de la construction du LEGO, je (Thomas) me suis lancé dans la conception électronique de celui-ci.

Pour cela, j’ai d’abord listé tout ce que je voulais intégrer à l’électronique :

  • 2 boutons,
  • 1 capteur de température,
  • 3 moteurs.

Pour m’aider dans la réalisation électronique, je me suis aidé de tous ces sites :

Allez c’est parti !

On commence par le branchement des boutons :

Les câbles blancs (entourés en rouge) récupèrent l’information de la pression du bouton et les câbles noirs (entourés en vert) s’occupe de fermer le circuit (de l’électricité passent dans le bouton, et il faut que l’électricité puisse faire une boucle). Si vous voulez, les boutons reçoivent de l’énergie par les câbles blancs et l’expulsent par les câbles noirs.

Concernant le branchement du capteur :

A droite à quoi ressemble le capteur de température de face et à gauche son branchement. La câble de gauche, en blanc récupère les mesures du capteur, celui du milieu en marron, alimente positivement le capteur (le capteur reçoit de l’énergie par ce câble) et le câble noir à droite du câble marron lui ferme la boucle (le capteur expulse l’énergie par ce câble.)

Et pour finir, les moteurs ont 3 câbles :

Le câble orange sert à contrôler le moteur, le rouge à l’alimenter et le marron à fermer la boucle. On refait ça 3 fois puisqu’on a 3 moteurs.

On branche tout ceci à la planche de test puis à l’ESP32, et voilà montage terminé :

Programmation du robot

Maintenant que le robot est construit et le montage électronique assemblé, il faut passer à la programmation de l’ESP32. Veuillez noter que depuis l’écriture de ce qui suit, le code a bénéficié de quelques mises à jours. Vous retrouverez l’archive en fin d’article avec les fichiers à jours.

Tout d’abord, il faut coder les briques de bases, qui vont nous permettre de manipuler différents outils, capteurs, actionneurs, api, etc.

  • C’est pour cela que nous créons un fichier buttons.py qui contient une classe Button dans laquelle nous initialisons 4 boutons : les 2 du microcontrôleur et les deux boutons ajoutés. Dans cette classe, vous verrez que chaque boutons a des caractéristiques supplémentaires. En effet il est possible de rendre un bouton poussoir équivalent à un levier. Nous créons différentes fonctions pour récupérer ou modifier des informations en rapport avec les boutons.
from machine import Pin
from time import sleep

# Importer les modules est inutile car ils sont déjà importés dans boot.py

class Buttons():
    def __init__(self):
        #self.name = "t-display-s3"
        
        # NomDuBouton : [                    PinUtilisé                     ,    mode    , value ]
        #      1      : [ Pin 3 en mode INPUT avec une résistance Pull-Down ,   levier   ,   0   ]
        # 1 : [ Pin(3, mode=Pin.IN, pull=Pin.PULL_DOWN), 1, 0 ]
        
        # Note : le chiffre de value dans la liste n'est pas pris en compte si le bouton est en mode "poussoir" -> On ne peut pas mettre None car si on décide de changer le mode du bouton, la valeur devient importante.
        
        
        # Pour une raison inconnue, "left" et "right" ne fonctionne plus.
        self.list_button = {"left" : [Pin(0, Pin.IN, Pin.PULL_UP),0,1],
                            "right" : [Pin(14, Pin.IN, Pin.PULL_UP),0,1],
                            1 : [Pin(2, mode=Pin.IN, pull=Pin.PULL_UP),0,1],
                            2 : [Pin(3, Pin.IN, Pin.PULL_UP),0,1]
                            }


    def getButton(self, button):
        return self.list_button[button]
    
    def setButton(self, button, pin, mode, value=None): # Le set fait également add.
        self.list_button[button] = [pin,mode,value]

    def setMode(self, button, val:bool): # Ce mode change le mode du bouton -> soit "levier" soit "poussoir"
        if isinstance(val, bool) or val in (0,1):
            self.list_button[button][1] = val
            if not(val):
                self.list_button[button][2] = 1
            return print("Mode définie sur : " + str(self.list_button[button][1]))
        else:
            raise ValueError("val must be a boolean or a integer included between 0 and 1.")
    
    def setValue(self, button, value):
        if self.list_button[button][1]:
            self.list_button[button][2] = value
            return print("Valeur définie sur : " + str(self.list_button[button][2])) 
        else:
            print("Button must be a toogle button, not a push button.")
            return False
    
    def getValue(self, button): # Renvoie la valeur du bouton
        if not(self.list_button[button][1]): # Si le statut de mode_toogle est égal à 0 <=> Si le bouton est en mode "poussoir" et non "levier"
            return self.list_button[button][0].value()
        else:
            if not(self.list_button[button][0].value()):
                self.list_button[button][2] = 0 if self.list_button[button][2] else 1
                sleep(0.25)
            return self.list_button[button][2]
    
    def isButtonPressed(self,button): # Renvoie True si le bouton est pressé (donc si value = 0) et False sinon
        return not(self.getValue(button))
  • Continuons avec le fichier temperature.py dans lequel nous avons une simple fonction qui nous renvoie les données récupérées par le capteur de température et d’humidité.
from time import sleep  #Inutile car importé dans boot.py

def getTemperatureAndHumidity(capteur): # Capteur doit etre sous cette forme : capteur = dht.DHT11(Pin(17)) -> Ne pas oublier d'importer le module dht
    try:
        capteur.measure() # Met à jour les données de températures et d'humidités
        sleep(1)
        return (capteur.temperature(),capteur.humidity())
    except:
        print("Valeurs non récupérées")
        return False
  • Egalement, à l’aide des fichiers motor.py et servo.py (trouvé sur internet), nous créons des fonctions qui réaliseront des déplacements particuliers :
from servo import Servo
import time

motor=Servo(pin=12) # A changer selon la broche utilisée
motorbd=Servo(pin=10)
motorbg=Servo(pin=11)
 
def droit(t=1):
    motor.move(285)
    time.sleep(t)

def bassin(t=2):
    motor.move(265)
    time.sleep(t)
    motor.move(305)
    time.sleep(t)

def baissebg(t=0.5):
    motorbg.move(335)
    time.sleep(t)
    
def levebg(t=0.5):
    motorbg.move(270)
    time.sleep(t)

def baissebd(t=0.5):
    motorbd.move(335)
    time.sleep(t)
    
def levebd(t=0.5):
    motorbd.move(270)
    time.sleep(t)
    
droit()
  • Pour finir, à l’aide du fichier connection.py, nous créons plusieurs fonctions en rapport avec la partie sans-fil de l’ESP32.
import network
import st7789 #En théorie inutile car déjà importé dans boot.py
from time import sleep

def connect_to_wifi(display, font, ssid, mdp):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        display.init()
        display.draw(font,"Connexion a un point",0,111)
        display.draw(font,"d'acces en cours",15,126)
        wlan.connect(ssid,mdp)
        while not wlan.isconnected():
            pass
    display.fill_rect(0,107,170,25,st7789.BLACK)
    display.draw(font,'Connecte !',40,111)
    sleep(0.8)
    display.fill_rect(0,107,170,10,st7789.BLACK)
    display.deinit()

def search_wlan():
    station = network.WLAN(network.STA_IF)
    station.active(True)
    return station.scan()

Passons aux modules qui vont utiliser des apis :

  • En premier lieu, nous avons le fichier geoloc.py dans lequel nous utilisons l’api de google qui à l’aide des réseaux wifi aux alentours est capable de situer l’ESP32. Dans ce fichier est présent une fonction qui récupère les coordonnées géographiques de l’ESP32.
from modules.connection import search_wlan
import ustruct as struct
import urequests as requests
import ujson as json
import modules.api_txt as api_txt


def getLocation(api_key): # Avec Google maps API + sécurité sur le nombre de requête.    
    
    if int(api_txt.get_api_counter()[0]) > 24000:
        print("Quota de demande dépassé !!! Vous ne pouvez pas faire de requête...")
        return False
    
    list_wlan = search_wlan()
    data = {
        "considerIp": False,
        "wifiAccessPoints": []
    }

    for wifi in list_wlan:
        entry = {
            "macAddress": "%02x:%02x:%02x:%02x:%02x:%02x" % struct.unpack("BBBBBB", wifi[1]),
            "signalStrength": wifi[3],
            "channel": wifi[2]
        }
        data["wifiAccessPoints"].append(entry)
    
    headers = {"Content-Type": "application/json"}
    url = "https://www.googleapis.com/geolocation/v1/geolocate?key=" + api_key
    response = requests.post(url, headers=headers, data=json.dumps(data))

    api_txt.add_api_counter(0)

    return json.loads(response.content)["location"]
  • En second et dernier lieu, il reste le fichier meteo.py qui va être capable d’utiliser une api de météorologie pour récupérer, analyser et mettre en forme des données en rapport avec la météo.
import urequests
import modules.api_txt as api_txt

def get_meteo(latitude, longitude): # Sur la prochaine heure
    
    if int(api_txt.get_api_counter()[1]) > 10000:
        print("Quota de demande dépassé !!! Vous ne pouvez pas faire de requête...")
        return False
    
    url = "https://api.open-meteo.com/v1/forecast?latitude={0}&longitude={1}&current=temperature_2m,precipitation&daily=temperature_2m_max,temperature_2m_min,precipitation_sum&timezone=auto&forecast_days=3".format(latitude,longitude)
    json_meteo = urequests.get(url).json()
    
    api_txt.add_api_counter(1)
    
    return {"current":
            {"temperature":
             [
                 json_meteo["current"]["temperature_2m"],
                 json_meteo["current_units"]["temperature_2m"]
                 ],
             "precipitation":
             [
                 json_meteo["current"]["precipitation"],
                 json_meteo["current_units"]["precipitation"]
                 ]
             },
            "demain":
            {"temperature": [round((json_meteo["daily"]["temperature_2m_min"][1] + json_meteo["daily"]["temperature_2m_max"][1]) / 2, 1), json_meteo["daily_units"]["temperature_2m_min"]],
             "precipitation":
             [
                 json_meteo["daily"]["precipitation_sum"][1],
                 json_meteo["daily_units"]["precipitation_sum"]
                 ]
             },
            "apres-demain":
            {"temperature": [round((json_meteo["daily"]["temperature_2m_min"][2] + json_meteo["daily"]["temperature_2m_max"][2]) / 2, 1), json_meteo["daily_units"]["temperature_2m_min"]],
             "precipitation":[
                 json_meteo["daily"]["precipitation_sum"][2],
                 json_meteo["daily_units"]["precipitation_sum"]
                 ]
             }
            }

Passons à la gestion graphique, nous avons récupérer le module tft_config.py fournit par les développeurs du pilotes graphiques pour l’esp32. Celui-ci permet de créer facilement une gestion de l’écran avec les bonnes caractéristiques. Nous n’afficherons pas le code ici, car ce n’est pas nous qui l’avons codé. Vous pouvez le retrouver dans l’archive à la fin de cet article ou ici.

Nous avons créé le fichier affichage.py qui contient un certain nombre de fonctions pour réaliser le rendu graphique pour l’ESP32. Il contient également le code qui récupère des images pour les animations du visage et des icones. Ces dessins et animations ont été réalisé par Ilana, ancienne première NSI.

import st7789
from time import sleep, localtime
import modules.motor as motor

last_temps = -1

def display_time(display,font,x,y):
    global last_temps
    temps = localtime()
    if last_temps < temps[5]:
        display.png("/images/icones/horloge.png",x,y,True)
        display.fill_rect(x+30,y+5,60+6,10,st7789.BLACK)
        display.draw(font, str(temps[3]) + ":" + str(temps[4]) + ":" + str(temps[5]),x+30,y+10)
    last_temps = temps[5]

def display_meteo(display,font,infos_meteo, x, y):
    display.draw(font,str(infos_meteo["temperature"][0]),x,y)
    display.draw(font, font.LAST,x + display.draw_len(font,str(infos_meteo["temperature"][0])), y)
    display.draw(font, " C",x + display.draw_len(font,str(infos_meteo["temperature"][0])), y)
    
    display.draw(font,str(infos_meteo["precipitation"][0]),x+80,y)
    display.draw(font,str(infos_meteo["precipitation"][1]),x + 80 + display.draw_len(font,str(infos_meteo["precipitation"][0])),y)

def display_temperature(display,font,infos_temperature,x,y):
    display.fill_rect(x,y,70,20,st7789.BLACK)
    display.png("/images/icones/temperature.png",x,y,True)
    display.draw(font,str(infos_temperature) + " C",x+30,y+9)
    display.draw(font,font.LAST,x+30+display.draw_len(font,str(infos_temperature)), + y+9)    
    
def meteo_icon(display,icone,x,y):
    display.fill_rect(x,y,50,50,st7789.BLACK)
    if icone == "pluie":
        display.png("/images/icones/mauvais_temps.png",x,y,True) #Nuage pluie gris pas content
    else:
        display.png("/images/icones/beau_temps.png",x,y,True) # Soleil content
        

def meteo_widget(display,font,infos_meteo,x,y):
    display.fill_rect(x,y,170,200,st7789.BLACK)
    
    
    display.draw(font, "Auj.",x,y)
    display_meteo(display,font,infos_meteo["current"],x, y+20) #x,y+20)
    
    display.draw(font, "Dem.", x, y+40) #x+50, y)
    display_meteo(display,font,infos_meteo["demain"], x, y+60) #x+50,y+20)
    
    display.draw(font, "Apr-Dem.", x, y+80) #x+90, y)
    display_meteo(display,font,infos_meteo["apres-demain"], x, y+100) #x+90,y+20)

def main_menu(display,font,infos_temp,infos_meteo):
    display_time(display,font, 10, 111) # x = 52 pour un semblant de centrage
    display_temperature(display,font,infos_temp,10,151)
    
    if infos_meteo["current"]["precipitation"][0] > 0:
        icone = "pluie"
        motor.baissebg()
        motor.levebd()
    else:
        icone = "soleil"
        motor.baissebd()
        motor.levebg()
    
    meteo_widget(display,font,infos_meteo,10,191)
    meteo_icon(display,icone,110,111)

def animation(display,name:str,iteration:int,i_fixe:int): # list_anim : {"content":(2,1),"endormi":(4,1),"somnole":(3,1),"observe":(0,0),"monocle":(2,1)}
    display.fill_rect(0,0,170,105,st7789.BLACK)
    for i in range(2,iteration+1):
        display.png("/images/" + name + "/" + name + str(i) + ".png",0,0)
        if name == "endormi":
            sleep(0.3)
    for i in range(iteration,0,-1):
        display.png("/images/" + name + "/" + name + str(i) + ".png",0,0)
        if name == "endormi":
            sleep(0.3)
    display.png("/images/" + name + "/" + name + str(i_fixe) + ".png",0,0)
    

Pour finir nous avons le fichier api_txt.py qui s’occupe de gérer des fichiers de textes importants pour le fonctionnement du programme, présents dans le dossier data.

"""
api[0] => Google Maps API
api[1] => Météo API
"""

def get_api_counter():
    fichier_r = open("data/counter-api.txt",'r')
    liste = fichier_r.read().split(",")
    fichier_r.close()
    return liste

def add_api_counter(indice_api): # ne fonctionne qu'avec deux apis
    fichier_w = open("data/counter-api.txt",'w')
    val = get_api_counter()
    val[indice_api] = int(val[indice_api]) + 1
    
    fichier_w.write(str(val[0]) + "," + str(val[1]))
    fichier_w.close()

def set_api_counter(indice_api,value): # ne fonctionne qu'avec deux apis
    fichier_w = open("data/counter-api.txt",'w')
    api_value = get_api_counter()
    api_value[indice_api] = str(value)
    fichier_w.write(api_value[0] + "," + api_value[1])
    fichier_w.close()

Maintenant que toutes nos briques sont construites, il ne reste qu’à écrire le fichier python principal qui va organiser tout le programme tel un chef d’orchestre.

Assemblage final

Maintenant, il faut simplement assembler notre montage électronique à notre construction LEGO. Puis à brancher notre ESP32 à un câble, lui injecter le code, et tout en le gardant brancher, le lancer, et la magie opère !

Fichiers

Veuillez noter que vous devrez ajouter votre propre clé api Google Maps API pour que la position soit mise à jour (dans boot.py à la ligne 21). Sinon vous pouvez changer manuellement la position dans le fichier old-location.txt se trouvant dans le dossier data. Veuillez respecter l’ordre suivant et ne surtout rien ajouter d’autre dans le fichier, pas même un espace :

latitude,longitude

Vous devrez également ajouter vos propres informations pour un point d’accès dans le fichier boot.py au niveau de la ligne 19 : wlan_info = ("SSID","MDP")
Le projet ne fonctionne pas sans une connexion à internet !!

Art

La Citrouille et la Tortue

Pour notre premier projet en classe de 1ère NSI, nous nous sommes basées sur le thème d’halloween.
Ici vous trouverez nos démarches pour créer cette image de style pop. Notre image est composée de quatre parties comportant chacune un fond et une citrouille de couleur différente.

Vidéo de présentation du projet :

Le projet :

Ce projet consiste à créer de l’art génératif grâce à un script python qui doit principalement utiliser des fonctions. Pour le réaliser, nous avons utilisé le module turtle pour tracer l’image et la fonction randint du module random pour pouvoir transformer notre image (.py) en png. Le thème de l’image générée était libre, elle pouvait même être abstraite !

Le processus de création :

Durant ce projet, nous sommes passées par plusieurs étapes pour concevoir l’image et répondre aux attendus.

Nous avons tout d’abord dû trouver une idée de l’image que l’on voulait créer. Pour cela, nous nous sommes inspirées de l’artiste américain Andy Warhol et de ses oeuvres comme « Shot Marilyns » et nous avons transposé son style dans le thème d’Halloween.

Après avoir dessiné un croquis à la main, nous avons créé le script d’une citrouille puis nous l’avons transformé en plusieurs fonctions pour pouvoir la reproduire en quatre exemplaires. Nous avons fait de même pour les rectangles.

Contrairement aux citrouilles et aux rectangles, nous avons tout de suite codé les lettres en tant que fonctions ce qui nous a permis d’aller plus vite dans la conception de l’image.

Enfin, nous avons dû organiser notre script rigoureusement pour que la tortue trace les éléments dans le bon ordre pour un rendu propre et travaillé.

Le code expliqué étape par étape :

Pour concevoir cette image, nous avons agi étape par étape. Nous avons donc relevé les différentes parties du code qui ont permis de tracer l’image. (Nous ne montrerons pas la mise en place des modules et comment nous avons généré des images automatiquement. Pour cela, vous pouvez vous rendre sur cet article.)

Le fond :

Tout d’abord, nous avons divisé l’écran en quatre parties égales et rectangulaires. Nous avons défini une fonction rectangle(x,y,color). Pour obtenir nos quatre parties distinctes, il suffit de mettre les coordonnées ainsi qu’une couleur en argument pour tracer les rectangles au bon endroit.

# trace les rectangles
def rectangle(x, y, color):
  up()
  goto(x, y)
  down()
  pencolor(color)
  fillcolor(color)
  begin_fill()
  for i in range(2):
    forward(640)
    right(90)
    forward(360)
    right(90)
  end_fill()

Nous appelons donc la fonction avec des coordonnées et des couleurs différentes pour tracer nos quatre rectangles.

rectangle(-640, 360, "#A8608E")
rectangle(-640, 0, "#25AE80")
rectangle(0, 360, "#C9BB32")
rectangle(0, 0, "#E1770C")

Résultat :

L’écriture « HAPPY HALLOWEEN » :

Ensuite, nous avons créé des fonctions pour chaque lettre différente composant l’expression « HAPPY HALLOWEEN » soit une fonction pour les lettres h, a, p, y, l, o, w, e et n. La lettre « o » est spéciale car nous avons voulu la représenter par une citrouille pour rester dans le thème d’Halloween. Ici, nous vous montrerons les fonctions lettre_h(x,y), lettre_o(x,y) et lettre_e(x,y).

# trace la lettre h en majuscule
def lettre_h(x,y):
  pensize(3)
  color("black")
  up()
  goto(x,y)
  setheading(90)
  down()
  forward(50)
  backward(25)
  right(90)
  forward(25)
  right(90)
  forward(25)
  backward(50)


# la lettre o est représentée par une citrouille
def lettre_o(x,y,f):
  pensize(3)
  color("black")
  fillcolor(f)
  up()
  goto(x,y+25)

  a = x
  b = y
  for i in range(2):
    up()
    goto(x, y)
    down()
    begin_fill()
    circle(25)
    end_fill()
    x += 25 / (35 / 15)
    y -= 25 / 14

  a = x + 25 / 1.4
  for i in range(2):
    up()
    goto(a, b)
    down()
    begin_fill()
    circle(25)
    end_fill()
    a -= 25 / (35 / 15)
    b -= 25 / 14

  goto(a + 25 / 14, b)
  begin_fill()
  circle(25)
  end_fill()

  setheading(0)
  up()
  goto(a, b + 25 * 2 + 2)
  down()
  pencolor("black")
  left(350)
  for i in range(4):
    left(40)
    forward(25 / (35 / 15))
    goto(a, b + 25 * 2 + 2)


# trace la lettre e en majuscule
def lettre_e(x,y):
  pensize(3)
  color("black")
  up()
  goto(x,y)
  setheading(90)
  down()
  forward(50)
  right(90)
  forward(25)
  backward(25)
  left(90)
  backward(25)
  right(90)
  forward(15)
  backward(15)
  left(90)
  backward(25)
  right(90)
  forward(25)

Après avoir créé toutes ces fonctions, nous les appelons dans un ordre précis pour écrire « HAPPY HALLOWEEN » et nous utilisons des boucles for pour que l’expression soit répétée et forme un motif. Nous rajoutons également la fonction stamp() qui permet de laisser l’empreinte de la tortue à la fin de la lettre « N ».

h = 20
for i in range(4):
  lettre_h(50,h)
  lettre_a(80,h)
  lettre_p(110,h)
  lettre_p(140,h)
  lettre_y(170,h)
  lettre_h(230,h)
  lettre_a(260,h)
  lettre_l(290,h)
  lettre_l(320,h)
  lettre_o(370,h,"#C9BB32")
  lettre_w(460,h)
  lettre_e(510,h)
  lettre_e(540,h)
  lettre_n(570,h)
  stamp()
  h += 90


h = -340
for i in range(4):
  lettre_h(50,h)
  lettre_a(80,h)
  lettre_p(110,h)
  lettre_p(140,h)
  lettre_y(170,h)
  lettre_h(230,h)
  lettre_a(260,h)
  lettre_l(290,h)
  lettre_l(320,h)
  lettre_o(370,h,"#E1770C")
  lettre_w(460,h)
  lettre_e(510,h)
  lettre_e(540,h)
  lettre_n(570,h)
  stamp()
  h += 90


h = 20
for i in range(4):
  lettre_h(-600,h)
  lettre_a(-570,h)
  lettre_p(-540,h)
  lettre_p(-510,h)
  lettre_y(-480,h)
  lettre_h(-420,h)
  lettre_a(-390,h)
  lettre_l(-360,h)
  lettre_l(-330,h)
  lettre_o(-280,h,"#A8608E")
  lettre_w(-190,h)
  lettre_e(-140,h)
  lettre_e(-110,h)
  lettre_n(-80,h)
  stamp()
  h += 90


h = -340
for i in range(4):
  lettre_h(-600,h)
  lettre_a(-570,h)
  lettre_p(-540,h)
  lettre_p(-510,h)
  lettre_y(-480,h)
  lettre_h(-420,h)
  lettre_a(-390,h)
  lettre_l(-360,h)
  lettre_l(-330,h)
  lettre_o(-280,h,"#25AE80")
  lettre_w(-190,h)
  lettre_e(-140,h)
  lettre_e(-110,h)
  lettre_n(-80,h)
  stamp()
  h += 90
shape("turtle")
stamp()

Résultat :

Le corps de la citrouille :

Puis, nous avons défini la fonction corps_citrouille(x,y,pen,fi) pour dessiner le corps de la citrouille. Celle-ci prend en paramètre la couleur et les coordonnées du corps. Le corps est constitué de plusieurs cercles assemblés dans un ordre précis pour que le remplissage s’effectue correctement.

# trace le corps de la citrouille
def corps_citrouille(x,y,pen,fi):
  pencolor(pen)
  pensize(5)
  fillcolor(fi)
  a = x
  b = y
  for i in range(2):
    up()
    goto(x,y)
    down()
    begin_fill()
    circle(70)
    end_fill()
    x += 30
    y -= 5

  a = x + 50
  for i in range(2):
    up()
    goto(a,b)
    down()
    begin_fill()
    circle(70)
    end_fill()
    a -= 30
    b -= 5

  goto(a + 5,b)
  begin_fill()
  circle(70)
  end_fill()

Nous appelons cette fonction avec des coordonnées et des couleurs différentes pour créer nos quatre corps de citrouille.

corps_citrouille(-380,110,"#E1C40C","#E3E013")
corps_citrouille(280,110,"#FF4017","#FF5733")
corps_citrouille(280,-260,"#502D9B","#6445A6")
corps_citrouille(-380,-260,"#26b510","#7EEb1E")

Résultat:

La tige de la citrouille :

De plus, nous avons conçu la tige à l’aide d’une autre fonction qui prend en compte les coordonnées et les couleurs de la tige. Pour la concevoir, nous avons d’abord tracé deux traits partant d’un même point puis nous les avons reliés pour effectuer le remplissage.

#  trace la tige de la citrouille
def tige_citrouille(x,y,pn,fl):
  setheading(0)
  up()
  goto(x,y)
  down()
  pencolor(pn)
  fillcolor(fl)
  begin_fill()
  right(160)
  for i in range(2):
    left(110)
    forward(30)
  goto(x,y)
  end_fill()
  begin_fill()
  right(20)
  for i in range(35):
    forward(1)
    right(2)
  end_fill()

Nous appelons ensuite cette fonction tige_citrouille(x,y,pn,fl) avec différentes coordonnées et couleurs pour bien positionner nos quatre tiges de citrouille.

tige_citrouille(-350,260,"#515146","#7A7A65")
tige_citrouille(310,260,"#0E6F12","#1B801F")
tige_citrouille(310,-110,"#8C0F37","#AD2953")
tige_citrouille(-350,-110,"#909207","#C2BD34")

Résultat :

Le visage :

Enfin, nous avons paramétré une fonction afin de lui créer un visage qui a pour arguments ses coordonnées. Le visage est composé de deux yeux qui sont des triangles, d’un nez qui est un triangle plus petit et d’une bouche également composée de formes géométriques.

# trace le visage de la citrouille
def visage(x,y):
  setheading(0)
  pensize(1)
  up()
  goto(x,y)
  down()
  pencolor("#000000")
  fillcolor("#000000")
  begin_fill()

# trace les deux yeux  
  for i in range(2):
    up()
    goto(x,y)
    down()
    for i in range(3):
      forward(30)
      left(120)
    x += 70
  end_fill()

# trace le nez 
  up()
  goto(x - 100,y - 22)
  down()
  begin_fill()
  for i in range(3):
    forward(22)
    left(120)
  end_fill()

# trace la bouche
  up()
  goto(x - 120,y - 35)
  down()
  begin_fill()
  l = 10
  for i in range(2):
    forward(l)
    right(45)
    forward(12)
    left(90)
    forward(12)
    right(45)
    l = 12
  forward(15)
  for i in range(90):
    forward(0.5)
    right(2)
  for i in range(2):
    right(45)
    forward(12)
    left(90)
    forward(12)
    right(45)
    forward(12)
  right(45)
  forward(12)
  left(90)
  forward(12)
  right(45)
  for i in range(90):
    forward(0.5)
    right(2)
  end_fill()

Nous devons donc appeler cette fonction visage(x,y) avec des coordonnées différentes pour aligner le visage sur le corps de chaque citrouille.

visage(-378,193)
visage(282,193)
visage(282,-177)
visage(-378,-177)

Résultat :

Les difficultés rencontrées :

La première difficulté a été de trouver la distance entre les lettres pour former l’expression « HAPPY HALLOWEEN ». Au début, nous les avions espacées de 30 pixels puis nous nous sommes aperçues que certaines lettres se chevauchaient. Après de nombreuses tentatives, nous sommes arrivées au rendu recherché.

Une autre difficulté a été de tracer les cercles qui composent le corps de la citrouille dans le bon ordre pour que le remplissage s’effectue correctement. En ayant compris la logique du remplissage nous sommes finalement parvenues à trouver cet enchaînement.

Enfin, nous avons dû trouver les emplacements de tous les éléments qui composent la citrouille (le corps, la tige et le visage). Ce processus a été très long et a nécessité de nombreux essais mais nous y sommes arrivées !

Sources :

Pour écrire ce script, nous avons utilisé nos connaissances ainsi que le site python.org pour trouver de nouvelles commandes en python. Nous avons également regardé des projets de l’année dernière comme ceux qui sont dans la catégorie art.

Image finale :

Voici le rendu final de notre projet après avoir fait des tests, corrigé nos erreurs et pris du plaisir à coder !

Télécharger le script en .py :

Ne vous inquiétez pas, nous avons pensé à vous ! Vous pouvez télécharger le script ci-dessous pour vous amuser à le modifier ou tout simplement le montrer à vos amis !

Art

Voiture de course 2D

« La compétence naît de la détermination à passer du stade du novice à celui du débrouillard, forgeant ainsi le chemin de l’expertise »

Par où commencer ?

Dans un premier temps, il faut trouver un thème à dessiner.

Quoi de mieux que de dessiner quelque chose qui nous passionne ; L’automobile pour ma part.

C’est partie pour aller sur paint et essayer des motifs. Puis au bout d’une dizaine d’essais

puisque je ne suis pas un dessinateur professionnel, j’arrive enfin à quelque chose de sympa pour moi.

La Programmation.

Une fois mon modèle  prêt, il me reste plus qu’à passer au codage !

La partie noire de ce projet pour moi, mais aussi la plus importante.

J’appréhendais ce moment en tant que débutant, j’avais seulement les simples bases de python et je me demandais comment j’allais faire, mais je me suis rappelé.

« La compétence naît de la détermination à passer du stade du novice à celui du débrouillard, forgeant ainsi le chemin de l’expertise. »

La première ligne était inefficace, mais au fil du codage, je commençais peu à peu à être en confiance.

Le châssis.

La partie la plus facile, mais la plus déterminante pour la suite.

Je commence par définir la fonction  « châssis » et au bout de quelques essais, j’arrive enfin à un début de dessin.

Ma première boucle for.

Pour ajouter des petits détails à ma voiture, j’ai décidé d’essayer de faire deux suspensions avec ma première boucle « for i in range »

Au début message d’erreur sur message d’erreur, mais grâce à la persévérance, j’ai réussi ma première boucle for qui m’a redonné motivation à continuer tout en m’apprenant les bases du python.

Un segment verticale où 4 cercle de diamètre 10 se déplace vers le haut de 7 sur l’axe de (y) à chaque range.

Code amortisseur:

def amortisseur():
    forward(35)
    Y =int(-155)
    for i in range(4):
        up()
        goto(360,Y)
        down()
        pencolor(Rouge)
        pensize(2)
        circle(10)
        up()
        Y = Y+7

On continue sur les boucles for.

Pour continuer, j’ai décidé d’ajouter un peu de contraste au dessin grâce à l’amélioration de mon codage que j’ai pu acquérir au cours du projet.

Au niveau des roues , j’ai ajouté des jantes à bâton avec la fonction (for i in range()) tout en rajoutant quelques détails comme la fumée au niveau de l’échappement et l’aileron a l’arrière de la voiture.

Code fume :

 def fume():
    Y=int(170)
    X=int(20)
    P=int(10)
    pensize(2)
    pencolor(black)
    for i in range(5):
            up()
            goto(Y,X)
            down()
            circle(P)
            X = X+10
            P = P+1
            for i in range(P):
                up()
                goto(Y,X)
                down()
                circle(P)
                Y= Y+8
                
    
print(fume())

Rendu final.

J’ai commencé ce projet en appréhendant mon niveau en code python.

Mais au fil des nombreuses fonctions définit et de nombreux essais, j’ai réussi à monter en niveau :

Au début, j’arrivais juste à donner des instructions à la tortue.

Pour finir par des boucles avec des instructions.

Donc ce projet m’a permis d’évoluer autant sur moi que mon niveau python m’apprenant qu’avec de la persévérance, on arrive à tous ou presque.

L’image n’est pas superbe, mais montre l’évolution Du débutant en herbe à l’expert débrouillard de python.

Vidéo du projet:

Télécharger le code :

Art

Sous les Rayons d’Or ; l’Arabie saoudite

L’art génératif est une fusion fascinante entre la créativité humaine et la puissance des algorithmes informatiques, nous vous invitons à plonger dans un univers où la machine devient artiste, créant des œuvres d’une beauté unique et imprévisible.

Notre Projet

Pour ce premier projet de NSI, nous avons décidé de représenter la ville de Riyad en python. Par conséquent, notre image rentre dans le thème de l’Architecture. Nous avons réalisé une ville assez moderne, avec des bâtiments de toutes formes et un soleil hors du commun, notre but étant de faire ressortir la ville luxueuse de Riyad, tout en apportant notre petite touche d’originalité. La vrais question étant comment nous sommes passé d’une simple idée à un résultat splendide ?

Les différentes étapes de la création de l’image

Pour être arrivé à notre résultat final, nous sommes passé par 4 étapes;

Tout d’abord voici le script python qui nous a permis d’obtenir un fond innovateur, les couleurs n’ont pas été choisi par hasard, en effet nous avons souhaité créer une ambiance qui évoque l’originalité sans oubliée le luxe notamment avec des arrondie et un dégradée de couleur reflétant l’étendue de la ville. Nous avons dans un premier temps définie la fonction disque pour dessiner un disque, rayon pour définir la taille des disques, x et y pour les coordonnées du centre et pour le dégradé de couleur nous avons fait appel a Background-color, enfin nous avons utilisé les fonctions arrondi_gauche et arrondi_droit pour dessiner des parties d’arcs.

def disque(rayon, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y-rayon)
    pendown()
    pencolor(couleur)
    fillcolor(couleur)
    begin_fill()
    circle(rayon)
    end_fill()
    

x, y = (0,-250)
radius = (700)
color = ("#FA1D65")
disque(radius, x, y, color) 
t    
x, y = (0,-250)
radius = (600)
color = ("#F72367")
disque(radius, x, y, color)     

   
x, y = (0,-250)
radius = (500)
color = ("#F02E6B")
disque(radius, x, y, color)

x, y = (0,-250)
radius = (400)
color = ("#EA3970")
disque(radius, x, y, color)     

x, y = (0,-250)
radius = (300)
color = ("#E73F72")
disque(radius, x, y, color)

def arrondi_gauche():
    for i in range(120):
        left(1)
        forward(5/40)
        
def arrondi_droit():
    for i in range(100):
        right(1)
        forward(5/80)

Le fond est certes beau, mais nous n’avions toujours pas trouvée cette touche de beauté supplémentaire, c’est ce pourquoi nous avons rajouté des étoiles, nous avons utilisé un code python assez simple. Nous avons commencée par définir une nouvelle fonction étoile, puis nous avons placés 125 points grâce à la boucle for i in range ces points sont placés aléatoirement grâce à randint.

def etoile():
    pensize(1)
    pencolor("white")
    for i in range(125):
        penup()
        x,y = randint(-700,700), randint(-0,700)
        goto(x,y)
        pendown()
        circle(1)

Il est désormais temps de passer, au bâtiment, pour ce qui est des bâtiments nous avons voulus représenter des grattes ciel luxueux de différentes formes, pour ce faire nous avons créer des fonctions bat 1, bat 2 etc … nous avons ensuite définis leur largeur, longueur et hauteur jusqu’à que les bâtiments ressemble à la forme qui nous convenais et enfin nous les avons placés à des endroits différents.

def bat1(longueur,largeur, couleur=(1,1,1)):
    pendown()
    pencolor(couleur)
    left(90)
    forward(longueur)
    right(90)
    forward(largeur)
    right(90)
    forward(longueur/1.5)
    left(90)
    forward(longueur/2)
 
def bat5(longueur, largeur, couleur=(1,1,1)):
    etage(longueur, largeur/5)
    etage(longueur/2 , largeur/5)
    etage(longueur/3 , largeur/5)
    left(80)
    forward(longueur/2)

def etage(longueur, largeur, couleur=(1,1,1)):
    left(90)
    forward(longueur)
    right(90)
    forward(largeur)

def etage_inverse(longueur, largeur, couleur=(1,1,1)):
    forward(largeur)
    left(270)
    forward(longueur)
    left(90)
def bat5_inverse(longueur, largeur, couleur=(1,1,1)):
    right(160)
    forward(longueur/2)
    left(80)
    etage_inverse(longueur/3, largeur/5)
    etage_inverse(longueur/2 , largeur/5)
    etage_inverse(longueur, largeur/5)

def bat3(longueur, largeur, couleur=(1,1,1)):
   forward(longueur/3)
   left(90)
   forward(longueur)
   right(90)
   forward(largeur)
   right(90)
   forward(longueur/4)
   left(90)
   forward(largeur)
   right(90)
   forward(longueur/2)
   
def bat4(longueur, largeur, couleur=(1,1,1)):
    left(90)
    forward(largeur/3)
    left(90)
    forward(longueur)
    right(90)
    forward(largeur)
    left(90)
    forward(longueur-30)
    right(90)
    forward(largeur+25)
    right(90)
    forward(longueur+20)
    
def bat2(longueur, largeur, couleur=(1,1,1)):
    left(90)
    forward(largeur)
    left(90)
    forward(longueur)
    right(90)
    forward(largeur)
    right(90)
    forward(longueur/2)
    left(45)
    forward(longueur/5)
    right(225)
    forward(longueur/8)
    right(90)
    forward(largeur+8)
    right(90)
    forward(longueur+8)

def bat6(longueur, largeur, couleur=(1,1,1)):
    left(90)
    forward(largeur)
    left(90)
    forward(longueur)
    right(90)
    forward(largeur)
    left(90)
    forward(longueur/3)
    right(90)
    forward(largeur)
    right(90)
    forward(longueur-130)
    left(90)
    forward(largeur)
    right(90)
    forward(longueur-50)
    left(35)
    forward(largeur*2)
    right(35)
    forward(longueur-75)

penup()
fillcolor("#313131")
begin_fill()
goto(-630,-450)
goto(-630,-450)
bat1(60,30, couleur=("#313131"))
bat5(150,80 , couleur=("#313131"))
bat5_inverse(150,80 , couleur=("#313131"))
bat3(100,50, couleur=("#313131"))
bat4(100,40, couleur=("#313131"))
bat2(180,65, couleur=("#313131"))
bat6(165,35, couleur=("#313131"))
left(90)

right(90)
bat4(150,50, couleur=("#313131"))
left(90)
bat3(150,50, couleur=("#313131"))
pendown()
goto(630,-460)
goto(-630,-450)
end_fill()

Enfin, il manquait la structure la plus importe, le soleil pour, le soleil on a imaginé un soleil assez original, nous l’avons d’abord dessinée, puis nous l’avons ensuite codée en python, pour ce faire nous nous sommes aidés du concours NumWorks et nous avons utilisé une boucle avec un pas de 1 qui dessine le soleil avec les instructions de forme et de taille donnés.

from math import *
penup()
goto(0, 0)
pendown()

for r in range(20, 80, 1):
    penup()
    pensize(4 if r % 2 else 1)
    for a in range(361):
        d = r + 5 * cos(radians(12 * a))
        x = d * cos(radians(a))
        y = d * sin(radians(a))
        goto(x, y)
        pendown() 
        

Image final

Les problèmes rencontrés

Il faut savoir que cette image n’est pas aussi facilement réalisable qu’elle ne le paraît, en effet nous avons rencontrés énormément d’obstacle à la création de cette image, notamment lorsque les bâtiments on été créer les tailles on été difficilement ajustable, nous avons dus rajouter plusieurs paramètres pour ajuster la taille des bâtiments ce qui nous a pris plusieurs heure. Nous avons également rencontrés de nombreux code d’erreur heureusement que toute les ressources étais à notre disposition, au final il suffisait de lire.

Notre ressentis sur ce premier projet

Nous avons beaucoup aimé ce premier projet, nous avons pris du plaisir à réaliser le projet, c’est une belle expérience et un coup de coeur, j’ai énormément appris en python pendant ses vacances et je trouve que ce qui est magique c’est d’apprendre en prenant du plaisir.

Télécharger le .py

Art

L’engrenage du temps

Dans notre projet de NSI de Novembre 2023, nos enseignants nous ont demandé de créer un programme informatique qui génère une image , en se concentrant sur le concept d’art génératif.

Présentation du sujet:

Partant du tableau des 42 thèmes abordés en classe, notre attention s’est focalisée sur la pendule, symbole puissant du temps. À partir de là, l’idée centrale du projet a émergé : “L’engrenage du temps” visant à représenter à l’aide de turtle notre image du temps .

Sur la représentation que nous avons, on peut observer plusieurs composants tels qu’une pendule, un engrenage, un dégradé et un disque. Nous allons les examiner en détail au fil de la présentation.

Analyse du script:

Le fond : 

def degradé():
    turtle.colormode(255)
    turtle.speed(0)
    global compt
    global fr
    global fg
    global fb
    while compt != 350 :
        turtle.pendown()
        turtle.pencolor(fr, fg, fb)
        turtle.pensize(3)
        turtle.forward(510)
        turtle.penup()
        turtle.right(180)
        turtle.forward(510)
        turtle.right(90)
        turtle.forward(1)
        turtle.right(90)
        fr += 1
        fg += 1
        fb += 1
        compt += 1
        if compt > 200:
            fr -= 1
            fg -= 1
            fb -= 1           

Pour commencer, nous avons programmer le fond avec un dégradé allant du noir au gris.

Dans la fonction dégradé il y a une boucle qui se répète 350 fois dans laquelle la tortue dessine ligne après ligne le dégradé en indentant les variables r,g,b de 1 a chaque ligne sans jamais dépasser la valeur 200.

Engrenages:

def n_engrenage(trait):
    turtle.left(90)
    turtle.forward(trait * 2)
    turtle.right(45)
    turtle.pencolor(couleur)
    turtle.pensize(8)
    turtle.fillcolor(couleur)
    turtle.begin_fill() 
    for i in range(12):
        turtle.forward(trait)
        turtle.right(120)
        turtle.forward(trait)
        turtle.left(90)
    turtle.end_fill()

La fonction “n_engrenage” dessine l’ engrenage à l’aide du module turtle.

La boucle “for” fait répéter 12 fois une suite d’instructions qui dessinent l’engrenage puis la fonction “turtle.fillcolor” viens définir la couleur de remplissage de l’engrenage qui débute et se finit grâce aux fonctions  “turtle.begin_fill” et “turtle.end_fill”.

Pendule: 

def disque(rayon, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y - rayon)
    pendown()
    pencolor(couleur)
    fillcolor(couleur)
    begin_fill()
    circle(rayon)
    end_fill()

x = 25  
y = 175
radius = 30
color = (255, 255, 0)  

disque(radius, x, y, color)

import turtle

pen = turtle
pen.penup()
pen.goto(65, 190)
pen.pendown()
pen.pencolor("black")
pen.goto(175, 350)

import turtle #fonction courbe

t = turtle

t.penup()
t.goto(90,173)
t.pendown()
t.right(50)
for i in range(55):
    t.forward(1)
    t.right(1)


import turtle #fonction courbe 2

t2 = turtle

t2.penup()
t2.goto(100,180)
t2.pendown()

t2.right(290)
for i in range(75):
    t2.forward(1)
    t2.right(1)


turtle.hideturtle()

Pour fabriquer la pendule, nous avons commencé avec un plan donné par nos professeurs de NSI. Il comportait plusieurs disques placés de façon aléatoire. Ensuite, nous l’avons simplifié en utilisant les instructions « begin_fill() » et « end_fill() » pour remplir les disques. Ensuite, nous avons ajouté une simple ligne en spécifiant un point de départ et un point d’arrivée. Enfin, nous avons inclus deux courbes pour donner l’impression que la pendule bougeait.

Les problèmes rencontrés:

Parmis les nombreux problèmes rencontrés il y avait que l’exécution de la fonction dégradé prenait très longtemps a finir de s’exécuter (env 1min), pour ne pas perdre temps à relancer tout le code a chaque modification on a trouvé la solution de mettre un “#” devant la ligne qui exécutait la fonction dégradé ce qui permettait de faire passer cette ligne pour un commentaire et de simplement enlever le  # quand on voulait exécuter le script complet.

L’image final:

Art

Le système solaire

Pour le projet que nous avons dû réalisé sur l’art génératif nous avons choisi le thème du système solaire de façon plus revisité.

La création du fond :

Le fond ici est un ensemble de cercle qui forme plusieurs dégrader de violés comme ci-dessous :

from turtle import *
import secrets

def disque(rayon, x, y, couleur=(1, 1, 1)):
  penup()
  goto(x, y-rayon)
  pendown()
  pencolor(color)
  circle(rayon)


#fond

list_color2=["#AC00D7", "#9B00C1", "#9A00C2", "#9800BF", "#8D00B0", "#8300A4", "#770095", "#690083", "#500064", "#400050", "#23002C", "#400050", "#500064",  "#690083", "#770095", "#8300A4", "#8D00B0", "#9800BF", "#9A00C2",  "#9B00C1", "#AC00D7",]

x = 0
y = -45
radius = 1100

color_index = 0

while radius >= 0:
    speed(400)
    color = list_color2[color_index] 
    fillcolor(color)
    begin_fill()
    disque(radius, x, y, color)
    end_fill()
    radius-=5
    color_index = (color_index + 1) % len(list_color2)</code>

Les axes de rotations des planètes :

Les axes ici en blanc représentantes le positionnement des astres dans le système solaire on les a codé en plusieurs étapes.

from turtle import *
import secrets

def disque(rayon, x, y, couleur=(1, 1, 1)):
  penup()
  goto(x, y-rayon)
  pendown()
  pencolor(color)
  circle(rayon)


#fond

list_color2=["#AC00D7", "#9B00C1", "#9A00C2", "#9800BF", "#8D00B0", "#8300A4", "#770095", "#690083", "#500064", "#400050", "#23002C", "#400050", "#500064",  "#690083", "#770095", "#8300A4", "#8D00B0", "#9800BF", "#9A00C2",  "#9B00C1", "#AC00D7",]

x = 0
y = -45
radius = 1100

color_index = 0

while radius >= 0:
    speed(400)
    color = list_color2[color_index] 
    fillcolor(color)
    begin_fill()
    disque(radius, x, y, color)
    end_fill()
    radius-=5
    color_index = (color_index + 1) % len(list_color2)


#cercle blanc

x, y = (0,-45)
radius = (410)
color = ("white")
disque(radius, x, y, color)   

x, y = (0,-45)
radius = (370)
color = ("white")
disque(radius, x, y, color) 

x, y = (0,-45)
radius = (330)
color = ("white")
disque(radius, x, y, color) 

x, y = (0,-45)
radius = (290)
color = ("white")
disque(radius, x, y, color) 

x, y = (0,-45)
radius = (230)
color = ("white")
disque(radius, x, y, color)

x, y = (0,-45)
radius = (200)
color = ("white")
disque(radius, x, y, color)

x, y = (0,-45)
radius = (170)
color = ("white")
disque(radius, x, y, color)

x, y = (0,-45)
radius = (140)
color = ("white")
disque(radius, x, y, color) 

La créations du soleil :

le soleil est l’astre qui nous permet de nous réchauffer donc les couleurs chaudes et vive est ce qui allait le mieux avec le soleil.

from turtle import *
import secrets

def disque(rayon, x, y, couleur=(1, 1, 1)):
  penup()
  goto(x, y-rayon)
  pendown()
  pencolor(color)
  circle(rayon)


#fond

list_color2=["#AC00D7", "#9B00C1", "#9A00C2", "#9800BF", "#8D00B0", "#8300A4", "#770095", "#690083", "#500064", "#400050", "#23002C", "#400050", "#500064",  "#690083", "#770095", "#8300A4", "#8D00B0", "#9800BF", "#9A00C2",  "#9B00C1", "#AC00D7",]

x = 0
y = -45
radius = 1100

color_index = 0

while radius >= 0:
    speed(400)
    color = list_color2[color_index] 
    fillcolor(color)
    begin_fill()
    disque(radius, x, y, color)
    end_fill()
    radius-=5
    color_index = (color_index + 1) % len(list_color2)


#cercle blanc

x, y = (0,-45)
radius = (410)
color = ("white")
disque(radius, x, y, color)   

x, y = (0,-45)
radius = (370)
color = ("white")
disque(radius, x, y, color) 

x, y = (0,-45)
radius = (330)
color = ("white")
disque(radius, x, y, color) 

x, y = (0,-45)
radius = (290)
color = ("white")
disque(radius, x, y, color) 

x, y = (0,-45)
radius = (230)
color = ("white")
disque(radius, x, y, color)

x, y = (0,-45)
radius = (200)
color = ("white")
disque(radius, x, y, color)

x, y = (0,-45)
radius = (170)
color = ("white")
disque(radius, x, y, color)

x, y = (0,-45)
radius = (140)
color = ("white")
disque(radius, x, y, color) 

#soleil

list_color=["yellow", "orange", "red", "orange", "yellow", "orange", "red"]

x = 0
y = -45
radius = 100

color_index = 0

while radius > 0:

    color = list_color[color_index]
    fillcolor(color)
    begin_fill()
    disque(radius, x, y, color)
    end_fill()
    radius-=1
    color_index = (color_index + 1) % len(list_color)


def arrondi_gauche():
  for i in range(180):
      left(1)
      forward(7/45)

def arrondi_droit():
  for i in range(180):
      right(1)
      forward(7/100)

Les planètes :

Un système solaire sans nos planètes n’est pas un vrai système solaire nous avons reproduit différent astres sur nos axes.

Art

Musique : The neighbourood wiped out !

En tant qu’admiratrices de l’album « wiped out ! » du groupe The Neighbourood, notre premier projet en NSI prend une dimension passionnante puisqu’on a choisi de représenter la pochette de cet album qui nous tient particulièrement à coeur.

Le commencement

Pour commencer, nous avons réalisé le fond noir et le cercle blanc qui se situe au centre. Nous avons ensuite placé le texte avec la bonne police d’écriture et la bonne taille au-dessus du cercle blanc. Nous devions aussi reproduire une petite maison à l’envers tout en bas de l’image en commençant par la base puis la cheminée et enfin le toit.

Les vagues et le sol

Pour créer les vagues, nous avons utilisé deux boucles for. Une première boucle sert à dessiner les arcs de cercle qui forment une ligne de vagues, puis une deuxième boucle permet de déplacer la tortue vers le bas pour dessiner la ligne de vagues suivantes. Cette approche répétitive nous permet de créer un motif de vagues continus en descendant d’une ligne à l’autre.

pensize(10)
goto(-300,0)
color("black")
pendown()
setheading(-35)

for i in range(9):
    for j in range(11):
         circle(35, 70)
         circle(-35, 70)
    penup()
    goto(-300, -20 * (i+1))
    pendown()

Il nous fallait aussi un sol qu’on a obtenu grâce à de nombreux cercles pour pouvoir ensuite déplacer la maison.

Le palmier avec les roches

Pour le palmier, on a d’abord dessiné des arcs de cercle pour créer le tronc incliné du palmier puis on a dessiné chaque feuille en utilisant la commande circle() avec des angles spécifiques pour créer la forme des feuilles. Les pierres ont été créées en combinant des formes géométriques superposées les unes aux autres.

L’oiseau

Pour dessiner un l’oiseau, on a dû crée les ailes, le corps, la queue, le bec et la tête de l’oiseau en utilisant des boucles, des lignes et des cercles.

Notre petite touche personnelle

Pour finir, on a rajouté notre petite touche personnelle, un ciel étoilé. Nous avons défini une fonction étoile pour générer 30 étoiles à des emplacements aléatoires.

def etoile():
    
    for i in range(5):
        forward(0.5)



pensize(2)
pencolor("white")

for i in range(30):
    x = randint(-400, 400)
    y = randint(100, 300)
    penup()
    goto(x, y)
    pendown()
    etoile()

CONCLUSION

Ce projet nous a permis de réaliser qu’on peut faire de très belle chose juste en ayant les bases de la programmation. On a pris beaucoup de plaisir à voir notre image se construire au fur et à mesure du temps. Cela nous a ouverts de nouvelles perspectives pour des projets futurs et nous avons hâte d’en apprendre davantage pour réaliser des créations encore plus techniques et impressionnantes.

RENDU FINAL

télécharger le .py

Art

Paysage japonais

Un paysage où un monument de la culture japonaise se mélange avec un fond naturel est magnifique. Et bien que le Japon soit le pays du soleil, levant nous allons le mettre en valeur avec, cette fois-ci, la lune, à travers des montagnes et des torii. Ces derniers symbolisent un portail entre l’enceinte sacrée et l’environnement profane. C’est pour cette raison qu’il y en a à l’entrée de certains sanctuaires.

Organisation du script

Notre script sera expliqué par étape, de l’arrière plan au premier. La fonction goto sera beaucoup utilisée afin de faire des tracés contrôlés ainsi que pensize() pour la largeur du tracé du stylo, color() pour choisir la couleur du crayon, circle() pour faire un cercle et for i in range pour faire des boucles.

L’arrière-plan

from turtle import*    
pensize (42)    
color("#FFFFFF")

a = -40   
col = ["#FFFFFF","#9BF7F7","#41F9FF","#00CDFF","#008FFF","#0059FF","#003AFF", "#4D00FF","#33079A", "#000000"]    
nb_col = len(col)    
color("#FFFFFF")   
goto(0, 50)    
circle(40)

a = 0  

for i in range (0,10,1):
    goto(0, 40-a)
    color(col[i%nb_col])
    circle(40+a)
    a = a +30
    goto(0, 40-a)
    circle(40+a)
    a = a +30
    goto(0, 40-a)
    circle(40+a)
    a = a +40

Ici, nous montrons que notre script est importé de Turtle, ce module nous permettant de contrôler un crayon pour tracer diverses formes géométriques.

Le fond de notre code n’est pas centré mais c’est fait exprès. Nous avons donc mis la taille du crayon à 42 avec la fonction : pensize (42), ensuite a = -40 nous a servis à déplacer le centre du cercle de base, soit, dans notre cas de le décaler vers la haut. Puis nous avons listé les différentes couleurs utilisées dans tous ces cercles avec col = [ ]. Le goto(0, 50) nous emmènera ensuite aux coordonnées données (donc (0, 50)) puis tracera des cercles de différentes tailles et de différentes couleurs grâce à la fonction cercle circle(40) et à la boucle for i in range (0,10,1):.

Les premières montagnes

color("#42392C")
goto(0, -80)
goto(100, 90)
goto(150,80)
goto(230,150)
goto(270,250)
goto(300,150)
goto(500,20)
goto(580,30)
goto(630,10)
goto(640,-40)
goto(0,-80) 

goto(0, -40)
goto(100,50)
goto(120,40)
goto(200,100)
goto(320,250)
goto(500,30)
goto(580,-10)
goto(630,20)
goto(640,-80)
goto(0,-70) 

goto(0, -40)
goto(120,40)
goto(200,80)
goto(320,230)
goto(500,00)
goto(580,00)
goto(630,000)
goto(640,-60)
goto(0,-50) 

goto(0, -40)
goto(120,33)
goto(200,60)
goto(320,210)
goto(500,00)
goto(580,00)
goto(630,000)
goto(640,-40)
goto(0,-20) 

goto(0, -40)
goto(120,20)
goto(200,50)
goto(320,190)
goto(500,00)
goto(580,00)
goto(630,000)
goto(640,-30)
goto(0,-10) 

goto(0, -40)
goto(120,00)
goto(200,20)
goto(320,170)
goto(500,00)
goto(580,00)
goto(630,000)
goto(640,-10)
goto(0,-00) 

goto(0, -40)
goto(120,00)
goto(200,10)
goto(320,160)
goto(580,00)
goto(630,000)
goto(640,-10)
goto(0,-00)  

goto(320,70)
goto(500,50)
goto(320,60)
goto(320,40)
goto(320,50)
goto(320,30) 

goto(0,-10)
goto(-200,50)
goto(-300,10)
goto(-430,160)
goto(-500,15)
goto(-600,250)
goto(-600,-10)

Nous comprenons ici que les goto sont et seront à l’honneur de ce script ! Ils sont simples à utiliser et donc à comprendre. La couleur des premières montagnes, elle, est encore une fois définie par color(« #42392C »).

Voici le résultat de tous ces goto ! Nous avons un début de montagne avec quelques imperfections mais pas d’inquiétudes ! Ce script fonctionne par plan donc comme un pochoir. Ici, les goto ont donc servis à diriger le stylo d’un point à un autre jusqu’à ce que nous ayons ce résultat

Encore des montagnes

color("#6F6556")
goto(0, 0)
goto(80,40)
goto(120,00)
goto(200,10)
goto(430,200)
goto(630,0)
goto(0,-00)

goto(0, 0)
goto(200,0)
goto(430,180)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(430,160)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(430,150)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,150)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,140)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,130)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,120)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,110)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,100)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,90)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,80)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,70)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,60)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,60)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,50)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,40)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,40)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,30)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,20)
goto(630,0)

goto(0, 0)
goto(200,0)
goto(400,10)
goto(630,0)

goto(0,0)
goto(0,-10)
goto(-200,30)
goto(-300,00)
goto(-400,150)
goto(-500,5)
goto(-600,200)
goto(-600,-10)
goto(0,0)

Ces montagnes sont faites de la même manière que les premières : le changement de couleur avec color(« #6F6556 ») et les goto ! Voyons maintenant le résultat en image…

Et voilà nos montagnes finies ! Mais il reste encore quelques étapes pour donner un sens à notre titre.

Le sol bleu

up()
goto(0,-400)
down()
col = ["#000000","#33079A","#4D00FF", "#003AFF", "#0059FF","#008FFF", "#00CDFF", "#41F9FF","#9BF7F7", "#FFFFFF","#9BF7F7","#41F9FF","#00CDFF","#008FFF","#0059FF","#003AFF","#4D00FF","#33079A","#000000"]
nb_col = len(col) 
recup = 0
k = -50
for i in range (0,50):
    color(col[i%nb_col])
    for i in range (0,3):
        k += 40
        goto(1000-k,-35)
        goto(1300-k,-500)

Comme pour l’arrière-plan, les couleurs sont définies grâce à col = [ ].

Avec l’image, nous pouvons plus facilement comprendre la présence de la boucle for i in range (0,50) qui nous a aidé à répéter tous les traits positionnés en diagonale pour nous éviter de les faire un à un avec des goto par exemple !

Les torii

pensize(42)
up()
goto(160,-220)
down()

color("#FF7000")
goto(160,100)
goto(-160,100)
goto(-160,-220)
goto(-160,100)
goto(-220,100)
goto(220,100)
goto(160,100)
goto(160,160)
goto(160,100)
goto(-160,100)
goto(-160,160)
goto(-220,160)
goto(220,160)

pensize(25)
up()
goto(80,-150)
down()

color("#E76E00")
goto(80,20)
goto(-80,20)
goto(-80,-150)
goto(-80,20)
goto(-120,20)
goto(120,20)
goto(80,20)
goto(80,60)
goto(80,20)
goto(-80,20)
goto(-80,60)
goto(-120,60)
goto(120,60)

pensize(12)
up()
goto(40,-120)
down()

color("#CB6100")
goto(40,-30)
goto(-40,-30)
goto(-40,-120)
goto(-40,-30)
goto(-60,-30)
goto(60,-30)
goto(40,-30)
goto(40,-10)
goto(40,-30)
goto(-40,-30)
goto(-40,-10)
goto(-60,-10)
goto(60,-10)

pensize(6)
up()
goto(20,-100)
down()

color("#A95100")
goto(20,-60)
goto(-20,-60)
goto(-20,-100)
goto(-20,-60)
goto(-30,-60)
goto(30,-60)
goto(20,-60)
goto(20,-50)
goto(20,-60)
goto(-20,-60)
goto(-20,-50)
goto(-30,-50)
goto(30,-50)

Voici le code de nos torii. A chaque nouveau pensize( ), il y en a un nouveau ! Nous avons d’abord le placement du crayon avec up( ), goto( ) et down( ) dans cet ordre là. Cela permet de se déplacer en décidant quand nous voulons écrire.

Cette fois-ci, l’image est sans le fond pour mieux observer le choix de couleur ainsi que les tailles décroissantes.

Le résultat final

Et enfin, notre résultat ! Nous avons également rajouté deux ligne, une verte et une marron pour montrer le début des montagnes mais ces lignes servent aussi à cacher la délimitation qu’il y avait pour les traits droits en diagonale.

Télécharger le .py

Projets

Astronomie : Planète face au soleil

L’astronomie fait partie intégrante de notre histoire en tant qu’être humain. On peut la percevoir comme une fenêtre ouverte sur l’immensité du cosmos. Chaque découverte, chaque image semble être ajoutée au puzzle infiniment grand et complexe qu’est l’univers.

Mise en avant de notre idée de projet

Tout d’abord, nous avons beaucoup réfléchi à la base de notre projet. Nous avons décidé de choisir l’astronomie, un domaine vaste et varié qui offre de nombreuses options. Nous avons opté pour une perspective de l’espace, imaginant une planète face au soleil avec sa « lune ». Notre inspiration est donc venue de la Terre.

Le fond

Dans un premier temps, concernant le fond, nous avons dû commencer obligatoirement par nous plonger dans l’ambiance de l’espace. Ainsi, nous avons choisi un fond noir. Pour parvenir à cela, nous nous sommes inspirés d’autres codes de création et en avons tiré ceci :

pensize(10000)
goto(100,1)
pensize(1)

Les étoiles

Après avoir ajouté le fond, il fallut combler ce vide. Quoi de mieux que des centaines d’étoiles ? Nous avons utilisé un script permettant de placer aléatoirement 400 étoiles de couleur blanche, de taille plutôt petite et identiques les unes aux autres, sur l’image avec ce script :

def etoile(longueur, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y)
    pendown()
    pencolor("white")
    fillcolor("white")

for i in range(5):
        forward(longueur)
        right(144)
        forward(longueur)
        left(72)
        
        
for y in range(400):
    x, y = randint(-800, 800), randint(-400, 400)
    longueur = 2
    couleur = (randint(247, 255), randint(127, 255), randint(0, 12))
    etoile(longueur, x, y, couleur)

Et voici le résultat des étoiles sur le fond noir :

Le soleil

Après avoir ajouté les étoiles, nous souhaitions inclure un élément principal à l’image : le soleil, élément majeur de notre existence et de celle de notre planète. Nous l’avons positionné en haut à gauche de l’image pour montrer son importance cruciale. Pour ce faire, nous avons ajouté un cercle à l’aide d’une fonction que nous avons nommée « planete », puis nous lui avons attribué des coordonnées, une couleur et une taille.

# Fonction de cercle
def planete(rayon, x, y, couleur):
    penup()
    goto(x, y)
    pendown()
    pencolor(couleur)
    fillcolor(couleur)
    begin_fill()
    circle(rayon)
    end_fill()    
    

# Dessiner le soleil
rayon_planete = 300  
x_planete, y_planete = -450, 30  
couleur_planete = "#FFC300"
planete(rayon_planete, x_planete, y_planete, couleur_planete)

Puis, nous avons souhaité intégrer une sorte de dégradé de couleur pour apporter réalisme et nuance. Après des recherches, nous avons finalement ajouté un cercle de couleur différente, de taille plus petite, au même emplacement que le soleil avec ce code :

# Dessiner dégradé
rayon_planete = 240
x_planete, y_planete = -450, 90
couleur_planete = "orange"
planete(rayon_planete, x_planete, y_planete, couleur_planete)

Et voici le résultat du soleil :

La planète

Après tout cela, l’image restait incomplète et semblait toujours trop vide. Nous avons donc ajouté une planète que nous avons placée à l’opposé du soleil pour donner l’impression qu’elle est en face de celui-ci. Nous avons décidé de représenter une planète bleue en référence à la Terre en utilisant la même fonction à celle utilisée précédemment pour dessiner un cercle. Voici le code utilisé :

# Dessiner Planete 
rayon_planete = 300
x_planete, y_planete = 500, -600
couleur_planete = "blue"
planete(rayon_planete, x_planete, y_planete, couleur_planete)

Et voici le résultat en rajoutant la planète :

La lune

Pour finaliser notre image, nous avons pensé qu’il manquait quelques détails à ajouter. Quoi de mieux que d’inclure une lune, que nous avons positionnée près de la planète bleue, pour accroître le réalisme. Pour créer cette lune, nous avons réutilisé la fonction des cercles. Nous avons défini sa couleur en gris et l’avons rendue plus petite, car elle est moins importante que le soleil et la planète. Ensuite, à l’intérieur de cette lune, nous avons ajouté deux cercles encore plus petits pour représenter des cratères, de couleur plus foncée pour les faire ressortir. Voici le code utilisé :

# Dessiner Lune
rayon_planete = 100
x_planete, y_planete = 370, 40
couleur_planete = "#d4d3d0"
planete(rayon_planete, x_planete, y_planete, couleur_planete)

# Dessiner cratère
rayon_planete = 20
x_planete, y_planete = 370, 80
couleur_planete = "#b4b1a8"
planete(rayon_planete, x_planete, y_planete, couleur_planete)
rayon_planete = 30
x_planete, y_planete = 320, 145
couleur_planete = "#b4b1a8"
planete(rayon_planete, x_planete, y_planete, couleur_planete)

Et voici le résultat finale :

Télécharger le .py

Art

Miata: une route de nuit

Il s’agit d’un projet d’art génératif réalisé en utilisant principalement le module turtle de python . Nous avons décider de générer une Mazda mx-5 aussi appelée Miata sur une route de nuit avec des étoiles générées aléatoirement.

Le script

On commence par importer les modules dont on a besoin pour la suite tout en définissant la taille de l’image grâce au module pillow

from turtle import *
from random import randint
try:
    from PIL import *
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False
# Uniquement des lettres, des chiffres, un tiret. Rien d'autre.
titre = "MIATA"
# Définir le titre de la fenêtre de turtle + propagande ^^
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")
# definir la taille de la fenêtre en 720p (Largeur, Hauteur)
# La tortue part du centre, au point de coordonnées (0,0)
setup(800, 700)
# La tortue va à la vitesse du lièvre et active sa cape d'invisibilité
speed(0)
hideturtle()

Image de fond

On génère ensuite l’image en commençant par le fond

On commence par le ciel de nuit et les étoiles

def star():

     pensize(3)
     seth(randint(0,360))
     e = randint(2,6)
     color("#F2F3CF")
     penup()
     goto(randint(-400,400),randint(85,350))
     pendown()
     for i in range(5):
        forward(e)
        right(142)

speed(0)
pensize(10000)
hideturtle()
color("#07073C")
forward(10)

for i in range(18):
    star()
penup()
goto(-250,300)
pendown()
pensize(2)
begin_fill()
seth(210)
color("#BFC298")
for i in range(

On commence par définir la fonction star() qui nous sert a générer une étoile d’une taille aléatoire a une position elle aussi aléatoire dans la zone qui sera le ciel pour éviter qu’elle finisse cachée par les autres éléments qui seront générés par la suite. Ensuite on fait un fond bleu nuit qui prend l’entièreté de l’écran en mettant une taille de crayon très grande et on génère 18 étoiles en utilisant une boucle qui répète la fonction star() puis on fait un croissant de lune en faisant des boucles donnant des arrondis . Ainsi on obtient cela:

on continue en ajoutant une forêt qui sera derrière la voiture

goto(-400,75)
pendown()
pensize(2)
color("#2C4D20")
begin_fill()
for i in range(3):
    seth(45)
    forward(77)
    right(90)
    forward(70)
    seth(45)
    forward(60)
    right(90)
    forward(80)
    seth(45)
    forward(50)
    right(90)
    forward(43)
right(45)
forward(460)
right(90)
forward(810)
right(90)
forward(455)
end_fill()

Ce code nous permet de générer des triangles de 3 tailles différentes que l’on va remplir fin de donner l’illusion d’une forêt dense en utilisant un vert foncé

On continue en réalisant la route

seth(0)
goto(-450,-125)
pensize(30)
color("#373639")
forward(900)
penup()

goto(-400,-175)
pendown()
pensize(2)
begin_fill()
color("#262527")
forward(800)
right(90)
forward(200)
right(90)
forward(800)
right(90)
forward(200)
end_fill()

goto(-400,-180)
pensize(7)
seth(0)
for i in range(4):
    color("white")
    forward(100)
    penup()
    forward(100)
    pendown()

Ce code nous permet de faire un grand trait de couleur grise pour représenter la barrière puis il génère un grand rectangle qui représente la route en y ajoutant des bandes blanches, créant ainsi une route.

La voiture

On passe ensuite à la partie la plus importante du script: la voiture

def phares():
    left(30)  
    fillcolor('gray')
    begin_fill()
    left(80)
    color('yellow')
    pensize(4)
    forward(25)
    pensize(1)
    color('gray')
    right(120)
    forward(30)
    right(130)
    forward(30)
    end_fill()
    forward(-31)
    color('red')
    pensize(4)
    right(50)
    forward(32)
    forward(-32)
    color('red')
    right(125)
    pensize(1)
    pendown()
def jantes():
    color('gray')
    begin_fill()
    fillcolor('black')
    circle(30)
    end_fill()
    forward(5)
    left(90)
    forward(10)
    pendown()
    begin_fill()
    fillcolor('silver')
jantes()

Cette partie nous permet de faire l’avant de la voiture en définissant la fonction pour faire une roue composée d’un pneu et d’une jante, inspiré des jantes BBS et ainsi que la fonction qui servira à faire le phare avant

pensize(1)
color('black')
fillcolor('red')
for i in range (35):
    forward(1)
    right(0.75)
right(75)

forward(50)
right(90)
for i in range(169):
    forward(0.5)
    left(1)
right(94)
forward(20)
end_fill()
right(85)
forward(52)
right(163)
penup()
forward(63)
pendown()
jantes()

Résultat final

Il s’agit de la fin du code pour l’image, terminant la Miata en faisant l’habitacle composé du toit de la voiture et d’une fenêtre puis l’arrière de la voiture en rappelant la fonction définie plus tôt afin de faire la seconde roue

Pour conclure on met la dernière partie de code pour exporter l’image en fichier.png

# ============== 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:]

# Génère un fichier .ps
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')

# Ouvre le fichier .ps et génère le fichier .png


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()

Télécharger le projet