bezier

 

Courbes de Bézier par David Cobac


Monsieur Bézier


Pierre Bézier (1912 - 2000) était un ingénieur français travaillant chez Renault. C'est lui qui est à l'origine de ces fameuses courbes, utilisées dans tous les logiciels de dessin vectoriel (mais aussi dans ce fabuleux langage qu'est metapost). Ces courbes permettent à partir de points dits << de contrôle >> de créer une courbe passant par les points extrêmes et déformable à souhait. Ce n'est donc pas un << matheux pur >> qui les a découvertes.


Un peu de théorie sur trois points


Soient P1, P2 et P3 trois points du plan et soit k un nombre réel de [0;1]. Notons Q1 le barycentre des points (P1;1-k) et (P2;k) et Q2 le barycentre des points (P2;1-k) et (P3;k). On construit finalement R(k) le barycentre de (Q1;1-k) et (Q2;k). Bien entendu, les positions des points Q1, Q2 et R dépendent du réel k choisi dans [0;1], Voici pour un choix de 3 points P1, P2 et P3 initiaux, la construction de R(0,5) et celle moins détaillée de R(0,7) :

On montre facilement que pour tout réel k de [0;1], R(k) est barycentre des points (P1;(1-k)^2), (P2;2k(1-k)) et (P3;k^2). Ainsi dans un système de coordonnées en dimension 2, on a : xR = (1-k)^2 * xP1 + 2k(1-k) * xP2 + k^2 * xP3 yR = (1-k)^2 * yP1 + 2k(1-k) * yP2 + k^2 * yP3 Pour k=0 nous sommes sur P1, pour k=1 sur P3. La courbe ne passe (généralement) pas par le point P2. De plus la courbe est tangente à la droite (P1P2) et la droite (P2P3). Les trois points sont appelés points de contrôle.


Implémentation avec trois points


Compte tenu de ce qui précède, l'implémentation se fait aisément :

 proc bezier3 { x1 y1 x2 y2 x3 y3 NbPoints } {
     for {set i 0} {$i<$NbPoints} {incr i} {
         set k [expr {$i*1.0/($NbPoints-1)}]
         set x [expr { pow(1-$k,2)*$x1 + 2*$k*(1-$k)*$x2 + pow($k,2)*$x3 }]
         set y [expr { pow(1-$k,2)*$y1 + 2*$k*(1-$k)*$y2 + pow($k,2)*$y3 }]
         lappend chemin $x $y
     }
     return $chemin
 }

Quelques explications On passe en arguments les 6 coordonnées et le nombre de points (R(k)) à construire. Le réel k est calculé en fonction du nombre d'étapes voulues. La procédure renvoie finalement la liste des coordonnées des points R(k) formant notre courbe. Sur un canevas Avec un widget canvas, voici un exemple de tracé :

 pack [canvas .c -width 300 -height 300 -bg white]
 .c create line [bezier3 0 0 300 0 300 300 100] -width 3


Tout pour quatre points


Dans le même esprit que pour trois points, on a avec des notations évidentes avec ce qui précède et en prenant S(k) pour point final : S(k) est barycentre des points (P1;(1-k)^3), (P2;3k(1-k)^2), (P3;3k^2(1-k)) et (P4,k^3). Ainsi dans un système de coordonnées en dimension 2, on a : xS = (1-k)^3 * xP1 + 3k(1-k)^2 * xP2 + 3k^2(1-k) * xP3 + k^3 * xP4 yS = (1-k)^3 * yP1 + 3k(1-k)^2 * yP2 + 3k^2(1-k) * yP3 + k^3 * yP4 La figure ci-dessous est assez explicite :

L'implémentation se fait aussi facilement qu'avec trois points :

 proc bezier4 { x1 y1 x2 y2 x3 y3 x4 y4 NbPoints } {
     for {set i 0} {$i<$NbPoints} {incr i} {
         set k [expr {$i*1.0/($NbPoints-1)}]
         set x [expr { pow(1-$k,3)*$x1 + 3*$k*pow((1-$k),2)*$x2 + 3*pow($k,2)*(1-$k)*$x3 + pow($k,3)*$x4}]
         set y [expr { pow(1-$k,3)*$y1 + 3*$k*pow((1-$k),2)*$y2 + 3*pow($k,2)*(1-$k)*$y3 + pow($k,3)*$y4}]
         lappend chemin $x $y
     }
     return $chemin
 }

Ainsi, en modifiant notre code précédent :

 pack [canvas .c -width 300 -height 300 -bg white]
 .c create line [bezier3 0 0 300 0 300 300 100] -width 3
 .c create line [bezier4 0 0 300 0 300 300 0 300 100] -width 3


Un peu de dynamisme avec 3 points


La plupart du temps, on veut que la courbe passe par les deux points extrêmes et puisse être contrôlé graphiquement, c'est donc typiquement le point P2 qu'il faut bouger. Je vous propose donc ce morceau de code :

 proc nouveau {} {
     .c delete all
     bind .c <1> "creer %x %y"
 }
 proc creer { x y } {
     # on cherche les points déjà créés
     set Points [.c find withtag point]
     set NbPoints [llength $Points]
     # on crée un point
     set dia 2
     .c create oval [expr {$x-$dia}] [expr {$y-$dia}]\\
         [expr {$x+$dia}] [expr {$y+$dia}] -fill black -tags point
     # si c'est le deuxième, on crée le segment qu'on va
     # pouvoir déformer...
     if {$NbPoints==1} {
         set XY [centreOvale $Points]
         .c create line [lindex $XY 0] [lindex $XY 1] $x $y\\
             -width 2 -tags A
         # une fois la courbe construite, on impose un nouvel
         # événementiel
         bind .c <1> ""
         bind .c <3> nouveau
     }
 }
 # cette procédure permet de trouver le centre d'un widget oval
 # et renvoie son abscisse et son ordonnée
 proc centreOvale {id} {
     set compo [.c coords $id]
     set absc [expr {([lindex $compo 0]+[lindex $compo 2])/2}]
     set ordo [expr {([lindex $compo 1]+[lindex $compo 3])/2}]
     return [list $absc $ordo]
 }

 proc debBezier { } {
     global sd sf bouge
     # on récupère les coordonnées des extrêmités
     # on en fait des variables globales
     set XY [.c coords current]
     set sd [lrange $XY 0 1]
     set sf [lrange $XY end-1 end]
     set bouge 1
 }
 proc milBezier { x y } {
     global sd sf bouge
     if !$bouge {return}
     # on modifie les coordonnées avec une courbe de Bézier
     .c coords current [eval bezier3 $sd $x $y $sf 100]
 }
 proc finBezier { } {
     global bouge
     set bouge 0
 }
 proc bezier3 { x1 y1 x2 y2 x3 y3 NbPoints } {
     for {set i 0} {$i<$NbPoints} {incr i} {
         set k [expr {$i*1.0/($NbPoints-1)}]
         set x [expr { pow(1-$k,2)*$x1 + 2*$k*(1-$k)*$x2 + pow($k,2)*$x3 }]
         set y [expr { pow(1-$k,2)*$y1 + 2*$k*(1-$k)*$y2 + pow($k,2)*$y3 }]
         lappend chemin $x $y
     }
     return $chemin
 }
 pack [canvas .c -width 300 -height 300 -bg white]
 # on fixe la variable de modification à 0
 set bouge 0
 # événementiel mouvement
 .c bind A <1> debBezier
 .c bind A  "milBezier %x %y"
 .c bind A  finBezier
 nouveau

Celui-ci permet de placer deux points sur un canevas, le deuxième placé le segment entre les deux se trace et on peut alors modifier l'allure de ce segment en modifiant à la souris le point de contrôle P2. Un appui sur le bouton droit de la souris permet alors de renouveler l'expérience.


[TV] C'est ne pas en français, mais cela peut être interessant en tout cas, la page avec un exemple de Bwise pour construire des subdivisions avec un graphe qui représente les calculs: [1] "Bwise deCasteljau algorithm example" . Si ma mémoire est bonne, Bezier ou deCasteljau travaillaient dans l'industrie automobile en France, quand en 1960 je pense on s'occupait avec les curves.


fab. : j'ai l'impression qu'il y a un gros probleme de securité ? comment se fait-il que n'importe qui puisse editer cet article ?

Kroc : Parce que c'est exactement le principe d'un wiki :^) Voir http://fr.wikipedia.org/wiki/Wiki pour plus d'informations.