deutsch     english    français     Imprimer

 

3.8 ART FILAIRE

 

 

INTRODUCTION

 
Vous avez certainement eu l’occasion de toucher un peu à l’art filaire à l’école enfantine. Rappelez-vous : il s’agissait de planter des clous à intervalles réguliers dans du carton selon un schéma bien défini et de les relier par des ficelles colorées pour faire apparaître de belles figures. Lorsque le nombre de clous était suffisamment grand, des courbes intéressantes se formaient aux endroits où les ficelles se densifiaient. En mathématiques, on parle d’enveloppe de la courbe car les fils sont tangents à la courbe.  
 De Täubner, Walz: Art filaire

Au lieu de créer ces dessins d’art filaire à la main, on peut aussi confier cette tâche à une machine. Cela impliquerait non seulement que la machine soit capable de comprendre nos instructions, mais encore qu’elle soit capable de les effectuer physiquement, par exemple à l’aide d’un bras robotisé qui puisse tirer des ficelles ou dessiner des traits rectilignes sur un écran. Un tel ensemble d’instructions constitue un algorithme. On pourrait imaginer des instructions de réalisation compréhensibles formulées en langage familier. Mais puisqu’il est souhaitable que la machine effectue exactement les mêmes opérations à chaque fois, l’algorithme doit être formulé de manière très précise, de sorte qu’il n’y ait aucune ambiguïté sur les opérations à effectuer à chaque étape. C’est exactement à cette fin que les langages de programmation ont été inventés et c’est la raison pour laquelle vous devez apprendre un langage  de programmation. En effet, les langages naturels ne permettent pas d’écrire une suite précise d’opérations de manière non ambiguë.


CONCEPTS DE PROGRAMMATION: Algorithmes, structures de données, modèle, élégance des programmes, listes, indices.

 

 

DES POINTS EN TANT QUE LISTES

 

Au lieu de travailler avec des planches, des clous et des ficelles, on va réaliser ces graphiques à l’ordinateur. Pour ce faire, on modélise la planche par la fenêtre, les clous par des points et les ficelles par des segments droits.

En transférant l’algorithme dans un langage de programmation, il est indispensable de se rapprocher le plus possible de la réalité. Les clous et les points géométriques représentent des objets tangibles pour l’homme et il devrait en être de même dans le programme.
 

En géométrie, on peut écrire un point par P(x,y)x et y sont ses coordonnées cartésiennes. Dans le programme, on peut regrouper ces deux nombres x et y dans une structure de données appelée liste. On écrit pour ce faire, p = [x, y]. Le point du plan P(0, 8) est donc modélisé par la liste p = [0, 8] .

On peut accéder aux éléments individuels d’une liste par leur indice qui indique leur position dans la liste, en commençant toujours à 0. On écrit l’indice à l’intérieur d’une paire de crochets carrés, à savoir p[0] pour la coordonnée x et p[1] pour la coordonnée y.

Ce qui est génial, c’est que l’on peut utiliser cette représentation de points du plan pour toutes les fonctions de GPanel qui nécessitent des coordonnées au lieu de devoir utiliser deux paramètres x et y indépendants. Le programme ci-dessous modélise le fait de tirer des ficelles par des allers-retours depuis le clou en A(0,8) vers le clou en B(0,-8) en passant par 19 clous situés sur l’axe des x. Il introduit un délai avec delay() pour que le traçage des ficelles soit observable par l’oeil humain.

from gpanel import *

DELAY = 100

def step(x):
    p1 = [x, 0]
    draw(p1)
    delay(DELAY)
    draw(pB)
    delay(DELAY)
    p2 = [x + 1, 0]
    draw(p2) 
    delay(DELAY)
    draw(pA) 
    delay(DELAY)

makeGPanel(-10, 10, -10, 10)
pA = [0, 8]
pB = [0, -8]
move(pA)
for x in range(-9, 9, 2):
    step(x)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Les données doivent également être structurées de manière appropriée dans l’implémentation d’un algorithme. Nos points sont modélisés par des listes de deux éléments représentant les coordonnées cartésiennes. Le choix de la structure de données affecte de manière significative l’ensemble du programme. Un célèbre professeur d’informatique de l’EPFZ a dit de manière très juste : « Programme = algorithme + structure de données » [Ref.]

Les listes peuvent stocker de nombreuses valeurs, appelées éléments de la liste, spécifiés dans l’ordre entre crochets carrés. On peut lire les éléments individuels d’une liste grâce à leur indice et leur assigner de nouvelles valeurs.

Toutes les commandes graphiques de GPanel fonctionnent également en modélisant les points comme des listes [x, y] représentant les coordonnées cartésiennes.

 

 

PROGRAMMER EST UN ART

 

Vous vous êtes probablement dit qu’on pourrait simplifier le précédent programme si on dessinait les lignes sans tenir compte du fait qu’il s’agit de ficelles dans la réalité. Au lieu de faire des allers-retours entre les points A et B comme on le ferait à la main avec des ficelles, il suffit de connecter les points A et B aux points intermédiaires de l’axe des x par des segments droits, ce qui simplifie grandement le programme.

from gpanel import *

makeGPanel(-10, 10, -10, 10)
pA = [0, 8]
pB = [0, -8]

for x in range(-9, 10, 1):
    pX = [x, 0]
    line(pA, pX)
    line(pB, pX)
 
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Un algorithme peut être implémenté de différentes manières qui se distinguent de manière significative au niveau de la longueur du code et du temps d’exécution du programme. On peut également parler de programmes plus ou moins élégants. Rappelez-vous seulement pour le moment qu’il n’est pas suffisant pour un programme de produire le résultat correct : l’élégance et le style sont également des critères très importants. Considérez la programmation comme un art !

 

 

DES ALGORITHMES D’ART FILAIRE ÉLÉGANTS

 

Lorsque l’on fait de l’art filaire sur l’ordinateur, il est souvent nécessaire de séparer un segment droit en deux segments qui sont dans un rapport bien précis avec la longueur du segment. GPanel met pour ce faire à disposition une fonction getDividingPoint(pA, pB, r) à laquelle on passe les deux extrémités pA et pB du segment à diviser ainsi que le rapport r entre les segments AP et AB. Cette fonction retourne une liste contenant les coordonnées du point délimitant les deux sous-segments

Le programme ci-dessous, particulièrement élégant, modélise un dessin d’art filaire comportant des clous sur les côtés AB et AC..

 
from gpanel import *

makeGPanel(0, 100, 0, 100)
     
pA = [10, 10]
pB = [90, 20]
pC = [30, 90]

line(pA, pB)
line(pA, pC)

r = 0
while r <= 1:
    pX1 = getDividingPoint(pA, pB, r)
    pX2 = getDividingPoint(pA, pC, 1 - r)
    line(pX1, pX2)
    r += 0.05
    delay(300)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Les fonctions mises à disposition par des bibliothèques externes, telles que getDividingPoint(), peuvent énormément simplifier un programme. Pour effectuer certaines tâches bien délimitées, vous devriez chercher à recourir à des fonctions de bibliothèques existantes que vous avez déjà rencontrées dans votre expérience de programmation, dénichées dans une documentation ou trouvées sur le Web.

Mathématiquement, la courbe émergeant du graphique précédent est une courbe de Bézier quadratique. On peut l’obtenir à l’aide de la fonction quadraticBezier(pB, pA, pC), où pB et pC sont les extrémités et pA est le point de contrôle de la courbe. La notion de courbe de Bézier est expliquée en matériel bonus en fin de section.

 

 

ART FILAIRE PILOTÉ PAR LA SOURIS

 

Modeling natural processes with the computer is not just a La modélisation de processus naturels est bien plus qu’un jeu car elle possède des applications très diverses. Il est possible de tester différentes situations facilement et rapidement jusqu’à en trouver une qui soit adaptée à être implémentée en pratique. Le programme est encore plus utile et attractif s’il est possible de changer le modèle en temps réel avec la souris. En Python, cela peut se faire très facilement en utilisant des fonctions de rappel. Dans le programme ci-dessous, on peut bouger le sommet A avec la souris.

 

Afin de créer le graphique, on utilise la fonction updateGraphics()qui est appelée par les gestionnaire d'événements de la souris. À chaque fois, on efface toute la fenêtre graphique et on la recrée en tenant compte de la nouvelle position du point A correspondant à la position du curseur de la souris.

from gpanel import *

def updateGraphics():
    clear()
    line(pA, pB)
    line(pA, pC)
    r = 0
    while r <= 1:
        pX1 = getDividingPoint(pA, pB, r)
        pX2 = getDividingPoint(pA, pC, 1 - r)
        line(pX1, pX2)
        r += 0.05

def myCallback(x, y):
    pA[0] = x
    pA[1] = y
    updateGraphics()

makeGPanel(0, 100, 0, 100, 
              mousePressed = myCallback,
              mouseDragged = myCallback)

pA = [10, 10]
pB = [90, 20]
pC = [30, 90]
updateGraphics()
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Il est possible sans problème de gérer deux événements différents, en l’occurrence l’événement de clic et l’événement de glissé (drag), en utilisant une seule fonction de rappel en guise de gestionnaire d’événement.

 

 

EXERCICES

 

1.

Reproduire le graphique filaire ci-contre

 

2.

Adapter le graphique filaire de l’exercice 1 pour pouvoir glisser le sommet du triangle avec la souris et redessiner le graphique en temps réel avec la nouvelle position du sommet.

 

 

 

 

MATÉRIEL EN BONUS


 

COURBES DE BÉZIER

 

Ces courbes ont été inventées dans les années soixante du siècle passé par Pierre Bézier qui était alors ingénieur chez Renault. Il cherchait à établir un modèle mathématique pour produire des courbes attirantes dans la conception de produits industriels.

On peut produire des courbes de Bézier par des graphiques filaires en utilisant l’algorithme de Casteljau.The algorithm reads as follows:

Voici les étapes de cet algorithme :

 
 

Spécifier 4 points P0, P1, P2, P3. (P0 et P3 seront les extrémités de la courbe tandis que P1 et P2 serviront de points de contrôle de la courbe).

Considérer les segments P0P1, P1P2, P2P3

Partager les segments P0P1, P1P2, P2P3 par les points Q1, Q2, respectivement Q3 dans le même rapport. On a donc l’égalité suivante : P0Q1/P0P1=P1Q2/P1P2=P2Q3/P2P3

Relier Q1 à Q2 et Q2 à Q3

Partager les segments Q1Q2 et Q2Q3 par les points R2 et R3 dans le même rapport que pour la troisième étape. On a donc Q1R2/Q1Q2=Q2R3/Q2Q3= P0Q1/P0P1=…

Relier R2 à R3.

On peut facilement implémenter cet algorithme dans un programme Python en représentant les points par des listes et en appelant plusieurs fois la fonction getDividingPoint() au sein de la boucle while qui est contrôlée par le rapport r.

from gpanel import *

makeGPanel(0, 100, 0, 100)
     
pt1 = [10, 10]
pc1 = [20, 90]
pc2 = [70, 70]
pt2 = [90, 20]

setColor("green")

line(pt1, pc1)
line(pt2, pc2)
line(pc1, pc2)

r = 0
while r <= 1:
    q1 = getDividingPoint(pt1, pc1, r)
    q2 = getDividingPoint(pc1, pc2, r)
    q3 = getDividingPoint(pc2, pt2, r)
    line(q1, q2)
    line(q2, q3)
    r2 = getDividingPoint(q1, q2, r)
    r3 = getDividingPoint(q2, q3, r)
    line(r2, r3)
    r += 0.05

setColor("black")
#cubicBezier(pt1, pc1, pc2, pt2)    
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Une courbe de Bézier cubique est définie par 4 points. Il est possible de dessiner de telles courbes avec la fonction cubicBezier() qui utilisera la couleur et l’épaisseur actuellement définies.

 

 

DESSIN INTERACTIF DE COURBES

 

En combinant judicieusement vos connaissances, vous êtes déjà capables d’écrire un programme assez professionnel permettant de créer des courbes de Bézier et de les modifier avec la souris de manière interactive. Le programme ci-dessous est même capable de détecter que le curseur de la souris se trouve à proximité de l’un des points, le colorie et permet de le déplacer en glissant la souris avec le bouton enfoncé.

Le programme doit traiter à de nombreuses reprises les 4 points, raison pour laquelle ils sont stockés dans la liste points qu’il est facile de parcourir avec une boucle for.
 

Pour déterminer le point que la souris est en train de manipuler, on utilise la variable active: pour stocker la position qu’occupe le point en question au sein de la liste points. Si aucun des points n’est manipulé par la souris, active prend la valeur -1.

from gpanel import *

def updateGraphics():
    # erase all
    clear()
 
    # draw points
    lineWidth(1)
    for i in range(4):
        move(points[i])
        if active == i:
            setColor("green")
            fillCircle(2)
        setColor("black")
        circle(2)

    # draw tangents
    setColor("red")
    line(points[0], points[1])
    line(points[3], points[2])

    # draw Bezier curve
    setColor("blue")
    lineWidth(3)
    cubicBezier(points[0], points[1], points[2], points[3])

def onMouseDragged(x, y):
    if active == -1:
        return
    points[active][0] = x
    points[active][1] = y
    updateGraphics()

def onMouseReleased(x, y):
    active = -1
    updateGraphics()

def onMouseMoved(x, y):
    global active
    active = near(x, y)
    updateGraphics()

def near(x, y):
    for i in range(4):
        rsquare = (x - points[i][0]) * (x - points[i][0]) + 
                     (y - points[i][1]) * (y - points[i][1])
        if rsquare < 4:
            return i
    return -1        

pt1 = [20, 20]
pc1 = [10, 80]
pc2 = [90, 80]
pt2 = [80, 20]
points = [pt1, pc1, pc2, pt2]
active = -1

makeGPanel(0, 100, 0, 100,
    mouseDragged = onMouseDragged,
    mouseReleased = onMouseReleased,
    mouseMoved = onMouseMoved)
updateGraphics()
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Il existe des structures de données assez complexes telles que des listes dont les éléments sont eux-mêmes des listes. Dans notre programme, on utilise cela pour stocker une liste de points qui sont eux-mêmes représentés par des listes. On peut alors accéder à la coordonnée x du point P1 en utilisant les doubles crochets points[1][0], thus with double brackets.

De nos jours, les courbes de Bézier sont utilisées de manière très abondante dans les programmes de dessin vectoriels et de conception assistée par ordinateur (CAO) [Ref.]

 

 

EXERCICES

 

1.

Le cœur ci-contre est engendré par deux courbes de Bézier symétriques possédant les mêmes extrémités ainsi que des points de contrôles symétriques. Commencer par dessiner ce cœur sur une feuille de papier en déterminant approximativement la position que doivent prendre les points de contrôle puis réaliser le dessin dans GPanel. Le remplissage est effectué à l’aide de la fonction fill(point, old_color, new_color),point représente un point qui se trouve à l’intérieur de la surface du cœur.