De l’Op art généré par un code python
L’Op Art, ou art optique est un mouvement artistique qui joue avec notre perception visuelle. Son origine remonte aux années 1920 mais il connaît un véritable essor dans les années 1960 avec des artistes comme Victor Vasarely et Bridget Riley. Ils utilisent notamment les contrastes de couleurs, la perspective, la répétition et l’alternance de formes géométriques, pour créer l’illusion de mouvement et de profondeur. Dans cet article, nous allons vous présenter une œuvre inspirée de ce mouvement et entièrement réalisée à l’aide d’un code écrit en langage Python avec le module Turtle.
L’inspiration de départ
Le problème avec les illusions d’optique est que ce que l’on voit, n’est pas la réalité. La première difficulté rencontrée a donc été de trouver un modèle avec des explications pour réaliser la figure. Après de nombreuses recherches, nous avons fini par trouver un programme de construction d’un couloir en damier sur le site d’une classe de CM2 (https://la-gazette-des-cm2-de-ducerceau.sitew.fr/Illusions_d_optique.qB.htm) dans le cadre d’un travail sur Victor Vasarely. Nous l’avons adapté pour respecter les dimensions de l’image finale (forme rectangulaire et non carrée).
Le programme de construction
Le programme de construction nous a permis d’identifier les différentes étapes à programmer :
- Partager les côtés du rectangle en intervalles de même longueur ;
- Tracer les segments qui relient deux points symétriques par rapport à l’origine ;
- Tracer les rectangles qui diminuent régulièrement en taille.
- Pour apporter notre touche personnelle, nous avons choisi de « peindre » les murs, le sol et le plafond du « couloir » avec des dégradés de couleurs.
Les variables modifiables seront la longueur, la largeur, les couleurs, le nombre d’intervalles, la taille des intervalles, le nombre de rectangles.
Programmation des couleurs
Nous avons commencé par les gradients de couleur en utilisant la méthode expliquée par Eric Schrafstetter dans sa vidéo « l’ART génératif, partie 1 – Transformations affines. »
Explication pour le « mur droit » :
- La tortue se déplace en abscisse de 0 à L/2. à chaque pas, elle va tracer un segment en couleur de bas en haut (cap = 90°).
- Pour une abscisse u donnée, ce segment part d’une diagonale et arrive sur l’autre. Donc on a créé les fonctions diag1 et diag2 qui retournent les ordonnées sur chaque diagonale.
def diag1(u): return largeur/longueur * u def diag2(u): return - largeur/longueur * u
- Entre 0 et L/2, la couleur varie de col_dep (couleur de départ = violet) à col_arr (couleur d’arrivée = jaune).
- Comme la couleur ne varie pas sur le même intervalle que l’abscisse de la tortue, on utilise une fonction affine qui transforme un point de l’intervalle [0 ; L/2] en un point de l’intervalle [col_dep ; col_arr] :
Voici la fonction codée en Python :
def T(u, a1, a2, b1, b2): return (b2 - b1)/(a2 - a1) * (u - a1) + b1
- Comme les dégradés ne sont pas dans le même sens ni de la même couleur suivant les triangles, nous avons créé une fonction « triangle » qui dépend de l’orientation de la tortue (cap) et des couleurs de départ et d’arrivée :
def triangle (cap, col_dep, col_arr): colormode(255) # pour pouvoir utiliser le mode RVB rvb = [0,0,0] pensize(2) u_fin= trunc(longueur/2) for u in range(u_fin): for k in range(3): rvb[k] = T(u, 0, u_fin, col_dep[k], col_arr[k]) pencolor((trunc(rvb[0]), trunc(rvb[1]), trunc(rvb[2]))) penup() if cap == 90 : goto(u,diag2(u)) pendown() goto(u,diag1(u)) elif cap == 180 : goto(u,diag1(u)) pendown() goto(-u,diag2(-u)) elif cap == 270 : goto(-u,diag2(-u)) pendown() goto(-u,diag1(-u)) elif cap == 0 : goto(-u,diag1(-u)) pendown() goto(u,diag2(u))
Programmation des « diagonales »
Pour simplifier, nous avons appelé aussi « diagonales », les segments qui relient un point et son symétrique par rapport à l’origine.
La tortue part de (-L :2 ; -l /2) à gauche, fait sa diagonale, revient à gauche en se décalant d’un intervalle vers le haut, fait sa diagonale, …
On procède de la même manière pour les diagonales de haut en bas, en partant de (-L/2 ; l/2).
On a donc d’abord créé une fonction « diag » qui sert à relier un point et son symétrique :
def diag(u, v): penup() goto(u, v) pendown() goto(-u, -v) penup()
Puis, nous avons programmé deux boucles, pour tracer les « diagonales gauche-droite » puis les « diagonales haut-bas » :
for i in range(nb_int2): diag(-longueur / 2, -largeur/2 + i*taille_int2) for j in range(nb_int1): diag(-longueur/2 + j*taille_int1, largeur / 2)
Programmation des rectangles
Nous avons codé une fonction « rectangle » où la tortue part du coin bas à gauche et tourne dans le sens inverse des aiguilles d’une montre :
def rectangle(L): # fonction permettant de tracer un rectangle penup() goto(-L/2, diag1(-L/2)) pendown() goto(L/2,diag2(L/2)) goto(L/2,diag1(L/2)) goto(-L/2,diag2(-L/2)) goto(-L/2, diag1(-L/2))
Pour faire diminuer la taille des rectangles de manière régulière, nous avons décidé d’appliquer un pourcentage de diminution de p% à la longueur (p est une variable globale qui peut être modifiée) :
t = float(longueur) while t > 10 : rectangle(t) t = t - t*p #la longueur diminue de p % à chaque itération
Pour l’extrémité du couloir, nous avons tracé un rectangle noir pour lequel nous avons essayé plusieurs dimensions et nous avons finalement choisi 160 pour des raisons esthétiques :
# rectangle final fillcolor('black') begin_fill() rectangle(160) end_fill()