Courbes de Bézier - John COLIBRI. | - résumé : construction interactive de courbes de Bézier
- mots clé : Bézier - Interpolation - cubiques - Editeur graphique
- logiciel utilisé : Windows XP, Delphi 6.0
- matériel utilisé : Pentium 1.400Mhz, 256 M de mémoire
- champ d'application : Delphi 1 à 2005 sur Windows
- niveau : développeur Delphi
- plan :
1 - Introduction
Pour réaliser des corrections de couleurs de bitmap, nous souhaitions pouvoir définir une courbe de correction. Outre les classiques translations, homotheties et diverses puissances, le plus souple est de laisser l'utilisateur
dessiner sa courbe avec la souris, puis d'interpoler les valeurs de cette courbe pour corriger les couleurs. Nous avons présenté dans un précédent article un éditeur
de points qui permet de créer une liste de points à l'écran les déplacer, effacer etc. Nous nous concentrerons donc ici uniquement sur la partie interpolation de Bézier: un série de points étant définie à l'écran, tracer la courbe de Bézier
correspondante, et interpoler n'importe quelle valeur. 2 - Courbes de Bézier Les courbes sont des des cubiques définies par 4 points de contrôle. Une droite
est définie par 2 points, un cercle (une quadratique) par 3 points, et une cubique par 4 points. Etant donnée 4 points que nous appellerons before_point,
start_point, end_point et after_point, les valeurs (l_x, l_y) des points entre start_point et end_point sont obtenues par:
for l_bezier_x:= 0.01 to 0.99 do begin
l_x:= f_smooth_bezier(l_bezier_x, 0)* l_before_point.m_x
+ f_smooth_bezier(l_bezier_x, 1)* l_start_point.m_x
+ f_smooth_bezier(l_bezier_x, 2)* l_end_point.m_x
+ f_smooth_bezier(l_bezier_x, 3)* l_after_point.m_x;
l_y:= f_smooth_bezier(l_bezier_x, 0)* l_before_point.m_y
+ f_smooth_bezier(l_bezier_x, 1)* l_start_point.m_y
+ f_smooth_bezier(l_bezier_x, 2)* l_end_point.m_y
+ f_smooth_bezier(l_bezier_x, 3)* l_after_point.m_y;
end; | où la fonction d'extrapolation f_smooth_bezier est:
Function f_smooth_bezier(p_parameter: Double; p_point_index: Byte): Double;
Begin case p_point_index of
0 : begin
p_parameter:= 1- p_parameter;
Result:= p_parameter* p_parameter* p_parameter/ 6
end;
1 : Result:= p_parameter* p_parameter* (p_parameter/ 2- 1)+ 4/6;
2 : Result:= p_parameter* (1+ p_parameter* (1- p_parameter))/ 2+ 1/6;
3 : Result:= p_parameter* p_parameter* p_parameter/ 6;
End; // case p_point end; // f_smooth_bezier |
Si nous avons une série de n points, il suffit donc pour tracer la courbe correspondante de répéter le calcul pour les points 2 à n-2.
Et pour trouver la valeur y correspondant à n'importe quelle valeur x, nous pouvons: - chercher quels sont les deux points située avant le point, et les deux points situés après le point
- avec un pas défini par l'utilisateur, rechercher la valeur de y correspondant à la valeur de x la plus proche
3 - Le programme Delphi 3.1 - La liste de points
Notre liste de points est définie par un c_2d_point_list (cf editeur_de_points ).
3.2 - Calcul de la transformée
Pour cette liste de points, nous calculons la liste des points situés sur la courbe de Bézier correspondante. Entre deux points nous pouvons placer autant de points intermédiaires que nous le souhaitons. Pour l'écran, il n'est pas
utile d'avoir plus de points que Round(longueur), mais si la courbe représente des données non graphique (une courbe de transfert pour une correction graphique), nous pourrions avoir besoin d'une interpolation plus fine.
Pour le dessin, nous fournissons la liste de points, et récupérons une liste des points interpolés. Cette seconde liste peut être utilisée pour visualiser la courbe de Bézier à l'écran.
3.3 - Interpolation
Pour l'interpolation, nous fournissons la liste de points, une valeur x et le nombre de points à utiliser entre les deux points. Nous récupérons la valeur y en deux étapes:
- nous recherchons quels sont les deux points situés juste avant et juste après le point cherché
- ayant trouvé ces deux points, nous recherchons pour quelle valeur du
paramètre de Bézier nous obtenons la valeur la plus proche de x. Pour ce paramètre, nous calculons la valeur y.
function f_interpolate_value(p_c_2d_point_list: c_2d_point_list;
p_x: Double): Double;
// -- assumes a FUNCTION (a single y value for each x)
function f_interpolate(p_point_index: Integer): Double;
// -- interpolate
var l_c_start_point, l_c_end_point: c_2d_point;
l_c_before_point, l_c_after_point: c_2d_point;
l_bezier_parameter, l_bezier_parameter_step: Double;
l_distance, l_x: Double; begin
with p_c_2d_point_list do
begin
l_c_before_point:= f_c_2d_point(p_point_index- 1);
l_c_start_point:= f_c_2d_point(p_point_index);
l_c_end_point:= f_c_2d_point(p_point_index+ 1);
l_c_after_point:= f_c_2d_point(p_point_index+ 2);
l_distance:= Sqrt(Sqr(l_c_end_point.m_x- l_c_start_point.m_x)
+ Sqr(l_c_end_point.m_y- l_c_start_point.m_y));
if l_distance= 0
then l_distance:= 1;
// -- at least a value for each integer x
// -- (for horizontal should be 1 / distance)
l_bezier_parameter_step:= 1/ l_distance/ 2;
l_bezier_parameter:= - l_bezier_parameter_step;
repeat
l_bezier_parameter:= l_bezier_parameter+ l_bezier_parameter_step;
l_x:= f_smooth_bezier(l_bezier_parameter, 0)* l_c_before_point.m_x
+ f_smooth_bezier(l_bezier_parameter, 1)* l_c_start_point.m_x
+ f_smooth_bezier(l_bezier_parameter, 2)* l_c_end_point.m_x
+ f_smooth_bezier(l_bezier_parameter, 3)* l_c_after_point.m_x;
until (l_bezier_parameter_step> 1) or (l_x> p_x);
Result:= f_smooth_bezier(l_bezier_parameter, 0)* l_c_before_point.m_y
+ f_smooth_bezier(l_bezier_parameter, 1)* l_c_start_point.m_y
+ f_smooth_bezier(l_bezier_parameter, 2)* l_c_end_point.m_y
+ f_smooth_bezier(l_bezier_parameter, 3)* l_c_after_point.m_y;
end; // with p_c_2d_point_list
end; // f_interpolate
var l_point_index: Integer;
begin // f_bezier_value // -- find the interval
with p_c_2d_point_list do begin
if f_2d_point_count= 0
then begin
Result:= 0; Exit;
end; l_point_index:= 0;
while l_point_index<= f_2d_point_count- 1 do
if f_c_2d_point(l_point_index).m_x> p_x
then Break
else Inc(l_point_index);
if l_point_index<= 1
then Result:= f_c_2d_point(0).m_y
else
if l_point_index> f_2d_point_count- 2
then begin
l_point_index:= f_2d_point_count- 1;
Result:= f_c_2d_point(l_point_index).m_y;
end
else Result:= f_interpolate(l_point_index- 1);
end; // with p_c_2d_point_list end; // f_interpolate_value
|
3.4 - La classe de transformation Comme nous fournissons toutes les données pour calculer la transformée ou interpoler un point, il n'y a aucune raison de créer une classe: un appel de
procédure ou de fonctions suffit. Toutefois, pour la correction de contraste de photo, nous souhaitions basculer d'une interpolation linéaire à une interpolation de Bézier. Le plus simple est
alors de créer une classe abstraite ayant les deux fonctions que nous souhaitons, puis dériver deux classes, l'une pour une poly-line et une autre pour Bézier. La classe abstraite est définie par:
c_transformer= class(c_basic_object)
function f_c_transform(p_c_2d_point_list: c_2d_point_list;
p_interpolation_count: Integer):
c_2d_point_list; Virtual; Abstract;
function f_interpolate_value(p_c_2d_point_list: c_2d_point_list;
p_x: Double): Double; Virtual; Abstract;
end; // c_transformer |
Et pour Bézier:
c_bezier_transformer= class(c_transformer)
constructor create_bezier_transformer(p_name: String);
function f_c_transform(p_c_2d_point_list: c_2d_point_list;
p_interpolation_count: Integer): c_2d_point_list; Override;
function f_interpolate_value(p_c_2d_point_list: c_2d_point_list;
p_x: Double): Double; Override;
end; // create_bezier_transformer |
Vu du côté utilisateur: - nous déclarons une variable (ou un champ) de type c_transformer
- nous créons soit un transformateur linéaire ou un transformateur de Bézier
- pour dessiner la courbe, nous appelons la fonction f_c_transform et dessinons la courbe correspondante
- pour connaître la valeur d'un point, nous appelons f_interpolate_value
Pour des exemples, voyez editeur_de_points ou correction_couleur
4 - Télécharger les sources Vous pouvez télécharger: - points_editor.zip : l'éditeur de points avec transformation de Bézier et le projet example (22 K)
Ce .ZIP qui comprend: - le .DPR, la forme principale, les formes annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque
.ZIP est autonaume)
Ces .ZIP contiennent des chemins RELATIFS. Par conséquent: - créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.
Comme d'habitude: - nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.
5 - L'auteur John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |