Dans le cadre du projet Art génératif qui consiste a générer une image en pythona l’aide du module turtle, j’ai réalisé ma voiture favorite, la Subaru Impreza dans un thème néon. Je vous explique dans cet article la rélisation de ce projet
Sommaire
La légendaire Subaru Impreza
L’Impreza est une compacte du constructeur automobile japonais Subaru, apparue en 1992. Icône de la marque grâce à ses succès en championnat du monde des rallyes (trois titres pilotes et trois titres constructeurs), elle en est à sa cinquième génération depuis 2016.Avec un moteur d’une cylindrée de 2 litres et 211 chevaux (94/97) et 217 (98/00) pour la version européenne de première génération sous l’appellation GT, les premières Impreza qui deviendront les WRX peuvent afficher des caractéristiques allant jusqu’à 320 chevaux dans leur version STI. Depuis 2015, La Subaru WRX est devenu un modèle indépendant de la Impreza et suit son propre développement. De ce fait, le nom Impreza disparait peu à peu des références sportives remplacé par la WRX et à moindre mesure par la Subaru BRZ.
Avant meme de realiser
Appel et verification des modules
from turtle import * from random import randint 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 = "Bastien.S" title(titre+" | Subaru Impreza WRC") setup(1920,1080) colormode(255) speed(0) hideturtle() flash = True if flash: wn = Screen() wn.tracer(0)
Ici on appelle les fonctions turtle et random ou plutot uniqument randint. On verifie ensuite on verifie si l’utilisateur a bien PIL d’installer s’il ne l’a pas un code d’erreur s’affichera ainsi qu’un lien pour installer le module. On definie le titre de la fenetre ainsi que sa taille,le type de couleurs utiliséeds (ici R,G,B), on cache la tortue, et randons le script instantané.
Un script en calques
Comme pour tout dessin numérique nous avons besoins de differents calques. Un calque est une couche transparente sur laquelle des éléments graphiques peuvent être placés et manipulés indépendamment les uns des autres. Les differentes fonctions représentent nos calques voici les notres :
background() batiments() road() subaru() logo()
Background()
def background(): r = 20 g = 20 b = 20 hauteur = 540 x = 0 for i in range(50): pensize(10),color(r,g,b),penup(),goto(-960,hauteur),pendown(),forward(1920) hauteur = hauteur - 8 b = b + 1 r = r + 1 goto(-960,hauteur) for i in range(37): pensize(10),color(r,g,b),penup(),goto(-960,hauteur),pendown(),forward(1920) hauteur = hauteur - 10 g = g + 2 r = r - 1 goto(-960,hauteur) for i in range(90): pensize(7),color(35,35,45),penup(),goto(-960,hauteur + 1),pendown(),forward(1950) hauteur = hauteur - 7
Nous avons décomposer fond en 3 parties distinctes: degradé noir vers rose puis rose vers vert pour le ciel puis du gris foncé pour la route.
Pour creer nos degradés nous plaçons notre tortue tout en haut a gauche de notre fenetre puis nous la faisons avancer jusqu’a l’extremité droite. Ensuite on retourne a gauche et on va a la ligne du dessous en diminuant la valeur (hauteur), tout ca grace a la fonction goto. On modifie la couleur en augmantant ou diminuant les valeur (R,G,B) et on recommence. Pour creer la route on fait la meme chose mais sans changer la couleur
batiments()
def batiments(): pensize(2) penup() goto(-960,-226) pendown() forward(100) color(20,20,20),forward(360),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(620),end_fill(),left(90),backward(80),begin_fill(),left(90),forward(650),left(90),forward(120),left(90) forward(40),right(70),forward(60),left(70),forward(592),end_fill(),backward(2),left(90),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(624),end_fill(),left(90),backward(260),color(30,30,30) begin_fill(),left(90),forward(640),left(100),forward(240),left(80),forward(599),end_fill(),left(90),forward(240),begin_fill(),left(90),forward(640),right(100),forward(240),right(80),forward(599),end_fill() left(90),forward(350),begin_fill(),left(90),forward(640),left(100),forward(240),left(80),forward(599),end_fill(),left(90),forward(240),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(620) end_fill(),color(20,20,20),begin_fill(),left(90),forward(1),left(90),forward(350),right(45),forward(80),right(95),forward(60),left(50),forward(35),left(25),forward(50),right(115),forward(383),end_fill(),begin_fill() left(180),color(25,25,25),forward(430),right(100),forward(80),right(80),forward(415),end_fill(),left(90),begin_fill(),color(15,15,15),forward(3),left(90),forward(250),right(90),forward(80),left(45),forward(20),right(45) forward(15),left(90),forward(60),right(90),forward(100),right(90),forward(325),end_fill(),left(90),backward(20),color(25,25,25),begin_fill(),left(90),forward(230),right(95),forward(100),left(95),forward(110),right(80) forward(100),right(55),forward(10),right(45),forward(342),end_fill(),begin_fill(),left(90),backward(15),left(90),color(10,15,15),forward(300),right(92),forward(120),right(88),forward(20),left(90),forward(20),right(90) forward(275),end_fill(),color(25,25,25),left(180),begin_fill(),forward(275),right(90),left(80),forward(20),right(65),forward(200),right(15),forward(100),right(90),forward(360),end_fill(),left(90)
Pour creer ses batiments sombre rien de plus simple et d’aussi long. Nous avons utilisé les fonctions bengin_fill et end_fill, avec differéntes teintes de gris pour créer du relief
road()
def road(): color(100,100,100),pensize(7) for i in range(2): liste_longueur_bandes1 = [2,10,6,10,60,150,] liste_longueur_bandes2 = [1,4,7,16,75,220] liste_longueur_bandes3 = [2,10,10,12,100,200] liste_longueur_bandes4 = [1,4,8,15,25,97] liste_longueur_distance_bandes = (1,3,6,9,20,40) liste_angle_gauche = [15,20,25,30,43,85] liste_angle1 = [90,50,45,35,45,85] liste_angle2 = [90,135,135,155,155,133] liste_angle3 = [90,70,40,20,25,45] liste_angle4 = [90,105,140,150,135,97] nblb1 = len(liste_longueur_bandes1) nblb2 = len(liste_longueur_bandes2) nblb3 = len(liste_longueur_bandes3) nblb4 = len(liste_longueur_bandes4) nbldb = len(liste_longueur_distance_bandes) nba = len(liste_angle_gauche) nba1 = len(liste_angle1) nba2 = len(liste_angle2) nba3 = len(liste_angle3) nba4 = len(liste_angle4) penup(),goto (-540,-226),pendown() for i in range(6): right(liste_angle_gauche[i%nba]) forward(liste_longueur_bandes1[i%nblb1]) left(liste_angle1[i%nba1]) forward(liste_longueur_bandes2[i%nblb2]) left(liste_angle2[i%nba2]) forward(liste_longueur_bandes3[i%nblb3]) left(liste_angle3[i%nba3]) forward(liste_longueur_bandes4[i%nblb4]) left(liste_angle4[i%nba4]) forward(liste_longueur_bandes1[i%nblb1]) penup() forward(liste_longueur_distance_bandes[i%nbldb]) pendown() left(liste_angle_gauche[i%nba]) color(255,255,255) pensize(2)
Pour realiser les bandes nous avons creer des listes contenants les differents parametres de celles ci (longueurs, angle, etc)
subaru()
def subaru(): color(0,150,250) pensize(7) for i in range(2): penup() goto(-330,-200) pendown() #roue arriere left(106),forward(95),left(60),forward(30),left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25),left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40) #arriere voiture right(155),forward(75),right(55),forward(20),left(45),forward(45),right(45),forward(50),right(25),forward(40) #aileron left(160),forward(30),right(80),forward(30),right(100),forward(60),backward(60),left(100),forward(8),right(100),forward(60),backward(60),left(80),forward(5),left(10),left(90),forward(20),left(90),forward(50),left(90),forward(35),left(20) #plafond forward(20),left(30),forward(80),right(22),forward(65),right(24),forward(85),right(9),forward(185),right(17),forward(70),backward(20) #parebrise right(165),forward(70),left(5),forward(145),left(112),forward(95) #capot left(50),forward(140),right(15),forward(35),left(5) #phare right(55),forward(32),backward(32),right(85),forward(35),left(90),forward(25),left(80),forward(120),left(110),forward(25),left(65),forward(85),right(10) #capot left(155),backward(35),left(20),backward(140),left(18),forward(150),left(35),backward(10),forward(20),right(35),forward(110),right(50),forward(10),backward(5),left(50),forward(15),right(40),backward(110) ,forward(110),left(23),forward(130),right(20),forward(50),right(35) #phare droit forward(20),right(10),forward(10),right(100),forward(60),right(100),forward(20),right(75),forward(50),penup(),goto(-330,-200),right(2),pendown() #roue arriere left(106),forward(95),left(60),forward(30) #dessus du peneu left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25) #jante left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40),left(37),forward(70),left(65),forward(55),left(60),forward(70) #bas de caisse right(58),backward(25),forward(190) #roue avant left(86),backward(45),right(78),forward(33) #jante penup(),left(85),forward(90),left(170),pendown(),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),penup(),left(48),forward(84),pendown(),left(96),backward(33),left(78),forward(112) #portiere left(10),forward(50),right(20),forward(55),left(35),forward(95),left(60),forward(75),left(35),forward(85),left(30),forward(70),left(124),forward(220),backward(140),left(67),forward(100),backward(180),right(140),forward(80),left(73),forward(140) #roue avant left(86),forward(20),right(22),forward(56),right(67),forward(50),right(67),forward(6),right(94),forward(47),left(47),forward(40),left(26),forward(62),left(27),forward(42),left(64),forward(71),left(60),forward(50),left(125),forward(15),right(77),forward(115) #parechock backward(20),right(108),forward(215),left(120),forward(40),right(120),forward(140),right(115),forward(38),left(115),backward(110),forward(190) #aeration parchock backward(45),penup(),right(90),forward(7),pendown(),right(90),forward(190),left(120),forward(10),right(120),penup(),forward(40),pendown(),forward(60),left(95),forward(20),left(85),forward(50),left(55),forward(25),right(55) penup(),forward(35),pendown(),right(60),forward(50),left(60),forward(130),left(60),forward(60),left(30),penup(),forward(7),pendown(),right(90),forward(45) #parechock right(90),backward(10),forward(42),right(90),forward(10),right(60),forward(20),right(120),forward(20),right(90),forward(17),right(90),forward(5),left(85),forward(50),right(86),forward(30),forward(160),left(5),forward(170),backward(170),right(5),backward(160),left(86),forward(40),right(25),forward(20),right(60),forward(57),right(50),forward(20),right(30),forward(40),backward(40),left(80),forward(30),right(85),forward(40),backward(25),left(85),forward(210),right(180),color(255,255,255),pensize(2)
Nous avons dit que les batiments etaient longs? Et bien ce n’est rien a coté de cette subaru. Cette subaru a été bètement dessiné
logo()
def logo(): pensize(4) def etoile_logo_main(): left(10),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),right(10) def etoile_logo(): left(10),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),right(10) color(255,255,0) for i in range(2): xetoile = [740,760,680,625,710] nbx = len(xetoile) yetoile = [400,350,350,305,310] nby = len(yetoile) for i in range(5): penup(),goto (xetoile[i%nbx],yetoile[i%nby]),pendown(),etoile_logo(),penup(),goto (560,380),pendown(),etoile_logo_main() pensize(2),color(255,255,255)
Afin de réaliser le logo Subaru nous avons créé deux fonction qui sont aussi simple que la subaru etoile_logo_main() qui correspond a la plus grande étoile et etoile_logo() a la plus petite. Avec goto() on a placer les etoiles a différentes coordonées.
Fin de script
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:] 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()
Ce script permet generer l’image final en .ps puis a la convertir en .png avec un nom aléatoir evitant que l’image précdédemment généré soit écrasé.
Script complet
from turtle import * from random import randint 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 = "Bastien.S" title(titre+" | Subaru Impreza WRC") setup(1920,1080) speed(0) hideturtle() flash = True if flash: wn = Screen() wn.tracer(0) # ============== MON SCRIPT PYTHON ============== colormode(255) def logo(): pensize(4) def etoile_logo_main(): left(10),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),left(60),forward(40),right(140),forward(40),left(60),forward(60),right(160),forward(60),right(10) def etoile_logo(): left(10),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),left(60),forward(20),right(140),forward(20),left(60),forward(30),right(160),forward(30),right(10) color(255,255,0) for i in range(2): xetoile = [740,760,680,625,710] nbx = len(xetoile) yetoile = [400,350,350,305,310] nby = len(yetoile) for i in range(5): penup(),goto (xetoile[i%nbx],yetoile[i%nby]),pendown(),etoile_logo(),penup(),goto (560,380),pendown(),etoile_logo_main() pensize(2),color(255,255,255) def background(): r = 30 g = 30 b = 30 y = 540 x = 0 for i in range(50): pensize(10),color(r,g,b),penup(),goto(-960,y),pendown(),forward(1920) y = y - 8 b = b + 1 r = r + 1 goto(-960,y) for i in range(37): pensize(10),color(r,g,b),penup(),goto(-960,y),pendown(),forward(1920) y = y - 10 g = g + 2 r = r - 1 goto(-960,y) for i in range(90): pensize(7),color(35,35,45),penup(),goto(-960,y + 1),pendown(),forward(1950) y = y - 7 def batiments(): pensize(2) penup() goto(-960,-226) pendown() forward(100) color(20,20,20),forward(360),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(620),end_fill(),left(90),backward(80),begin_fill(),left(90),forward(650),left(90),forward(120),left(90) forward(40),right(70),forward(60),left(70),forward(592),end_fill(),backward(2),left(90),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(624),end_fill(),left(90),backward(260),color(30,30,30) begin_fill(),left(90),forward(640),left(100),forward(240),left(80),forward(599),end_fill(),left(90),forward(240),begin_fill(),left(90),forward(640),right(100),forward(240),right(80),forward(599),end_fill() left(90),forward(350),begin_fill(),left(90),forward(640),left(100),forward(240),left(80),forward(599),end_fill(),left(90),forward(240),begin_fill(),left(90),forward(640),right(100),forward(100),right(80),forward(620) end_fill(),color(20,20,20),begin_fill(),left(90),forward(1),left(90),forward(350),right(45),forward(80),right(95),forward(60),left(50),forward(35),left(25),forward(50),right(115),forward(383),end_fill(),begin_fill() left(180),color(25,25,25),forward(430),right(100),forward(80),right(80),forward(415),end_fill(),left(90),begin_fill(),color(15,15,15),forward(3),left(90),forward(250),right(90),forward(80),left(45),forward(20),right(45) forward(15),left(90),forward(60),right(90),forward(100),right(90),forward(325),end_fill(),left(90),backward(20),color(25,25,25),begin_fill(),left(90),forward(230),right(95),forward(100),left(95),forward(110),right(80) forward(100),right(55),forward(10),right(45),forward(342),end_fill(),begin_fill(),left(90),backward(15),left(90),color(10,15,15),forward(300),right(92),forward(120),right(88),forward(20),left(90),forward(20),right(90) forward(275),end_fill(),color(25,25,25) left(180),begin_fill(),forward(275),right(90),left(80),forward(20),right(65) forward(200),right(15),forward(100),right(90),forward(360),end_fill(),left(90) t = -200 u = -330 def subaru(): color(0,150,250) pensize(7) for i in range(2): penup() goto(u,t) pendown() #roue arriere left(106),forward(95),left(60),forward(30),left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25),left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40) #arriere voiture right(155),forward(75),right(55),forward(20),left(45),forward(45),right(45),forward(50),right(25),forward(40) #aileron left(160),forward(30),right(80),forward(30),right(100),forward(60),backward(60),left(100),forward(8),right(100),forward(60),backward(60),left(80),forward(5),left(10),left(90),forward(20),left(90),forward(50),left(90),forward(35),left(20) #plafond forward(20),left(30),forward(80),right(22),forward(65),right(24),forward(85),right(9),forward(185),right(17),forward(70),backward(20) #parebrise right(165),forward(70),left(5),forward(145),left(112),forward(95) #capot left(50),forward(140),right(15),forward(35),left(5) #phare right(55),forward(32),backward(32),right(85),forward(35),left(90),forward(25),left(80),forward(120),left(110),forward(25),left(65),forward(85),right(10) #capot left(155),backward(35),left(20),backward(140),left(18),forward(150),left(35),backward(10),forward(20),right(35),forward(110),right(50),forward(10),backward(5),left(50),forward(15),right(40),backward(110) ,forward(110),left(23),forward(130),right(20),forward(50),right(35) #phare droit forward(20),right(10),forward(10),right(100),forward(60),right(100),forward(20),right(75),forward(50),penup(),goto(u,t),right(2),pendown() #roue arriere left(106),forward(95),left(60),forward(30) #dessus du peneu left(50),forward(30),backward(30),right(50),backward(30),right(60),forward(25),left(85),forward(40),left(65),forward(25) #jante left(2),forward(30),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),left(37),forward(40),left(37),forward(70),left(65),forward(55),left(60),forward(70) #bas de caisse right(58),backward(25),forward(190) #roue avant left(86),backward(45),right(78),forward(33) #jante penup(),left(85),forward(90),left(170),pendown(),left(10),forward(45),left(24),forward(40),left(90),forward(35),left(62),forward(60),left(45),forward(35),left(90),forward(37),penup(),left(48),forward(84),pendown(),left(96),backward(33),left(78),forward(112) #portiere left(10),forward(50),right(20),forward(55),left(35),forward(95),left(60),forward(75),left(35),forward(85),left(30),forward(70),left(124),forward(220),backward(140),left(67),forward(100),backward(180),right(140),forward(80),left(73),forward(140) #roue avant left(86),forward(20),right(22),forward(56),right(67),forward(50),right(67),forward(6),right(94),forward(47),left(47),forward(40),left(26),forward(62),left(27),forward(42),left(64),forward(71),left(60),forward(50),left(125),forward(15),right(77),forward(115) #parechock backward(20),right(108),forward(215),left(120),forward(40),right(120),forward(140),right(115),forward(38),left(115),backward(110),forward(190) #aeration parchock backward(45),penup(),right(90),forward(7),pendown(),right(90),forward(190),left(120),forward(10),right(120),penup(),forward(40),pendown(),forward(60),left(95),forward(20),left(85),forward(50),left(55),forward(25),right(55) penup(),forward(35),pendown(),right(60),forward(50),left(60),forward(130),left(60),forward(60),left(30),penup(),forward(7),pendown(),right(90),forward(45) #parechock right(90),backward(10),forward(42),right(90),forward(10),right(60),forward(20),right(120),forward(20),right(90),forward(17),right(90),forward(5),left(85),forward(50),right(86),forward(30),forward(160),left(5),forward(170),backward(170),right(5),backward(160),left(86),forward(40),right(25),forward(20),right(60),forward(57),right(50),forward(20),right(30),forward(40),backward(40),left(80),forward(30),right(85),forward(40),backward(25),left(85),forward(210),right(180),color(255,255,255),pensize(2) def road(): color(100,100,100),pensize(7) for i in range(2): liste_longueur_bandes1 = [2,10,6,10,60,150,] liste_longueur_bandes2 = [1,4,7,16,75,220] liste_longueur_bandes3 = [2,10,10,12,100,200] liste_longueur_bandes4 = [1,4,8,15,25,97] liste_longueur_distance_bandes = (1,3,6,9,20,40) liste_angle_gauche = [15,20,25,30,43,85] liste_angle1 = [90,50,45,35,45,85] liste_angle2 = [90,135,135,155,155,133] liste_angle3 = [90,70,40,20,25,45] liste_angle4 = [90,105,140,150,135,97] nblb1 = len(liste_longueur_bandes1) nblb2 = len(liste_longueur_bandes2) nblb3 = len(liste_longueur_bandes3) nblb4 = len(liste_longueur_bandes4) nbldb = len(liste_longueur_distance_bandes) nba = len(liste_angle_gauche) nba1 = len(liste_angle1) nba2 = len(liste_angle2) nba3 = len(liste_angle3) nba4 = len(liste_angle4) penup(),goto (-540,-226),pendown() for i in range(6): right(liste_angle_gauche[i%nba]) forward(liste_longueur_bandes1[i%nblb1]) left(liste_angle1[i%nba1]) forward(liste_longueur_bandes2[i%nblb2]) left(liste_angle2[i%nba2]) forward(liste_longueur_bandes3[i%nblb3]) left(liste_angle3[i%nba3]) forward(liste_longueur_bandes4[i%nblb4]) left(liste_angle4[i%nba4]) forward(liste_longueur_bandes1[i%nblb1]) penup() forward(liste_longueur_distance_bandes[i%nbldb]) pendown() left(liste_angle_gauche[i%nba]) color(255,255,255) pensize(2) #calque =================================== background() batiments() road() subaru() logo() #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:] 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()