ink-blog | Le blog d'un développeur ActionScript 3

Courbes de Bezier (deuxième étape).

Salut, Salut…

Voici la suite de mes tribulations à travers le monde merveilleux des courbes de Bezier :)

Pour commencer, intéressons nous à la première classe la plus « basique » j’ai nommée BezierCubic.
Cette classe est une classe de données, elle ne gère, ni ne génère aucun visuel. Elle permet simplement de récupérer des informations concernant une courbe donnée, définie par 4 points, deux ancres et deux points de contrôles.

Il est important de noter qu’elle gère les courbes de Bézier comme Illustrator ; c’est-à-dire que lorsque l’on modifie une ancre, le point de contrôle qui y est associé est également modifié, pour « suivre » la dite ancre.

Bon! Passons au choses sérieuses… Comment est ce que ça marche?

Pour commencer l’instanciation :

  1. var bc:BezierCubic = new BezierCubic();

Comme ça, nous venons de créer notre première instance de courbe de Bezier cubique. Nous pouvons déjà l’utiliser mais il faut avouer qu’en l’état elle n’est pas très intéressante.
Pour qu’elle ressemble un peu plus à ce que nous voulons en faire, il faut lui affecter les valeurs souhaitées pour les ancres et les points de contrôle.
Pour ce faire, deux méthodes, la premiere, qui correspond à notre cas présent et qui est aussi la plus laborieuse, consiste à affecter les propriétés anchorA, anchorB, controlA et controlB à la main comme ceci :

  1. bc.anchorA = new Point(100,100);
  2. bc.anchorB = new Point(400,100);
  3. bc.controlA = new Point(150,150);
  4. bc.controlB = new Point(350,50);

NOTE IMPORTANTE : comme la modification des ancres déplace les points de contrôle il est impératif de les définir après les ancres sinon vous risquez d’avoir des surprises (les valeurs par défaut de ancre A et B étant respectivement (0,0) et (1,1) :).

Maintenant que nous avons une belle courbe comme nous le voulons et que nous nous sommes bien fatigués à écrire ces… 5 lignes de code… voyons comment raccourcir tout ça!

Alors là, comme on peut s’en douter, il suffit de passer les points en paramètre du constructeur dans l’ordre ancre A, ancre B, contrôle A, contrôle B. Ainsi nous obtenons le constructeur suivant :

  1. var bc:BezierCubic = new BezierCubic(new Point(100,100), new Point(400,100), new Point(250,150), new Point(350,50));

Maintenant que nous avons vu quelques trivialités passons aux choses un peu plus sérieuses et examinons ce que cette classe peut faire avec un passage en revue des méthodes publiques qui la composent. Il y en a une petite dizaine :

Une des plus importantes, si ce n’est LA plus importante,

  1. getPointAtRatio(t:Number):Point

prend en paramètre une valeur réelle comprise entre [ 0, 1] et retourne le point de la courbe correspondant. Sachant que 0 correspond à l’ancre A et 1 à l’ancre B.
Grâce à cette méthode on peut par exemple tracer la courbe en utilisant une boucle dont chaque itération tracera un trait entre les différents point de celle-ci. Comme dans la fonction suivante.

  1. function drawCourbe(s:Shape, curve:BezierCubic):void{
  2.         var pas:Number = .001
  3.         s.graphics.clear();
  4.         s.graphics.lineStyle(1, 0xff0000);
  5.         s.graphics.moveTo(curve.getPointAtRatio(0).x, curve.getPointAtRatio(0).y);
  6.         for(var i:Number = 0 ; i <= 1 ; i+= pas){
  7.                 var p:Point = curve.getPointAtRatio(i);
  8.                 s.graphics.lineTo(p.x, p.y);
  9.         }
  10. }

On peut aussi l’utiliser pour faire une animation suivant une courbe de Bézier en utilisant une Tween par exemple.

  1. var monObjetAAnimer:DisplayObject = new MonObjetAAnimer();
  2. var TweenBezierProp:Number = 0;
  3. var tw:Tween = new Tween(this, "TweenBezierProp", None.easeInOut, 0,1, 2, true);
  4. tw.AddEventListener(TweenEvent.MOTION_CHANGE, _twUpdate);
  5. function twUpdate(e:TweenEvent):void{
  6.         var p:Point = bc.getPointAtRatio(_TweenBezierProp);
  7.         _monObjetAAnimer.x = p.x;
  8.         _monObjetAAnimer.y = p.y;
  9. }

En suite, dans la famille Utile nous avons la sœur

  1. getRotationAtRatio(t:Number):Number

qui donne l’angle de la tangente à la courbe au point de ratio t (piouf….) . On peut alors avoir un objet qui s’oriente suivant la dite courbe. Pour cela il suffit de rajouter dans la fonction twUpdate la ligne suivante

  1. _monObjetAAnimer.rotation = bc.getRotationAtRatio(_TweenBezierProp);

Ce qui donne :

  1. function twUpdate(e:TweenEvent):void{
  2.         var p:Point = bc.getPointAtRatio(_TweenBezierProp);
  3.         _monObjetAAnimer.x = p.x;
  4.         _monObjetAAnimer.y = p.y;
  5.         _monObjetAAnimer.rotation = bc.getRotationAtRatio(_TweenBezierProp);
  6. }

Les méthodes suivantes sont tout aussi utiles, mais de façon peut-être plus anecdotique.

La plupart d’entre elles ont été créées pour afficher du texte le long de la courbe.
Pour pouvoir afficher du texte il faut pouvoir positionner les lettres les unes à la suite des autres en fonction de la taille des lettres.
Pour cela, les méthodes

  1. getPointAtDistanceFrom(pStartRatio:Number, pDistance:Number, pStep:Number = 0.001):Point
  2. getRatioAtDistanceFrom(pStartRatio:Number, pDistance:Number, pStep:Number = 0.001):Number

permettent, respectivement :
- De trouver le point de la courbe qui se situe à une distance (pDistance) du point au ratio de départ (pStartRatio).
- Et de trouver le ratio du point de la courbe qui se situe à une distance (pDistance) du point au ratio de départ (pStartRatio).

Ces méthodes fonctionnent de la manière suivante :

en commençant à partir de « pStartRatio » elle calcule la distance de proche en proche avec un pas (pStep) jusqu’à ce qu’elle dépasse la distance indiquée (plus pStep est petit plus le calcul est précis mais plus il est long à réaliser).

La courbe est parcourue dans le sens ancre A vers ancre B sauf si pStep est négatif. Dans ce cas la courbe est parcourue dans le sens ancre B vers ancre A.

Ensuite, nous avons les méthodes permettant de calculer les distances sur la courbe. Méthodes utilisées dans celles qui précédent.

  1. getApproachLengthBetweenRatio(begin:Number, end:Number, pas:Number = .001):Number
  2. getApproachLength(pPas:Number = .001, force:Boolean = false):Number

La première permet de calculer la longueur de la courbe entre deux ratios suivant un pas donné (le pas par défaut est le pas optimal d’après mes observations).
La deuxième calcule la taille de la courbe entière. Cette valeur n’est réellement recalculée que lorsqu’un des points ou le pas a changé ou encore lorsque le paramètre force est défini sur true. Sinon la dernière valeur connue est utilisée.

La méthode getApproachLength peut être utiliser dans la fonction de dessin pour trouver un pas adéquat. Si les coordonnées de la courbes sont en pixel, getApproachLength retourne le nombre approximatif de pixels composant la courbe. Ainsi un pas p de 1/getApproachLength revient à, à peu près dessiner des segments d’un pixel de long (à noter cependant qu’il s’agit d’une estimation les points de la courbe n’étant pas répartis de manière uniforme en fonction du ratio).

Pour en finir avec les méthodes : la suivante

  1. getSubCurveDefinitionAtRatio(t:Number):Array

retourne un tableau à 2 dimensions contenant les définitions des deux sous-courbes composant la courbe principale à un ratio t.
Le tableau est de la forme

  1. _array[0]["anchorA"];
  2. _array[0]["anchorB"];
  3. _array[0]["controlA"];
  4. _array[0]["controlB"];

avec 0 pour la courbe côté ancre A et 1 pour la courbe côté ancre B.
(à noter que cette méthode peut évoluer en utilisant une classe supplémentaire contenant les 4 propriétés en lecture seule et en renvoyant un vecteur typé et limité en taille).

Maintenant que nous avons vu les principales méthodes de cette classe BezierCubic, voici quelques informations complémentaires.
Cette classe étend EventDispatcher et distribue des événements du type BezierEvent lorsque les différents points sont modifiés. On peut alors mettre à jour facilement des éléments graphiques les représentant ou représentant la courbe.

Il y a donc 4 évènements

  1. BezierEvent.CONTROL_A_CHANGE;
  2. BezierEvent.CONTROL_B_CHANGE;
  3. BezierEvent.ANCHOR_A_CHANGE;
  4. BezierEvent.ANCHOR_B_CHANGE;

Dans un prochain post je vous présenterai deux autres classes qui permettent d’implémenter une courbe composée de plusieurs courbes de Bézier.

Pour l’heure voici les sources ;)

PS : n’oubliez pas de mettre tous les imports nécessaires. ;)

Édit : Suite aux conseilles de Logic j’ai optimisé le code. Les temps de traitements sont beaucoup plus courts.
Vous pouvez utiliser les méthodes getPointAtRatio et getPointAtDistanceFrom en passant en paramètre final un Point qui sera modifié directement sans création d’une nouvelle instance, ce qui divise par 6 le temps de traitement sur la fonction getPointAtRatio par exemple.

There are 1 Comments to "Courbes de Bezier (deuxième étape)."

  • charles dit :

    Merci de partager ces classes très utiles !! Quel bonheur de manipuler ces jolies courbes à volonté !

Write a Comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

 

Essentials