Author: Christian.Queinnec@lip6.fr

## Animation

L'animation d'un graphisme à l'écran reprend des techniques de dessins animés. La majeure partie du dessin ne change pas, seule la partie animée doit modifier la couleur des pixels qui la compose. Un des problèmes immédiatement rencontré provient de la vitesse d'animation. Celle-ci peut varier selon la complexité du calcul et la vitesse d'exécution du processeur. Ainsi une application graphique animée pour être portable et donner le même effet doit tenir compte de la rapidité du processeur. Pour avoir un rendu fluide il est préférable d'afficher la nouvelle position de l'objet animé, puis d'effacer l'ancienne, en tenant compte de leur intersection.

##### Déplacement d'un objet

Nous simplifions le problème du déplacement d'un objet en choisissant des objets de forme simple que sont les rectangles. La difficulté restante est de savoir réafficher le fond d'écran une fois l'objet déplacé.
On cherche à faire évoluer un rectangle dans un espace clos. L'objet se déplace selon une vitesse en X et Y, quand il rencontre un bord de la fenêtre graphique, il rebondit selon son angle d'incidence. On se place dans une situation sans recouvrement entre la nouvelle et l'ancienne position de l'objet. La fonction `calc_pv` calcule, à partir d'une position `(`x`,`y`)`, de la taille de l'objet `(`sx`,`sy`)` et d'une vitesse `(`dx`,`dy`)`, la nouvelle position et la nouvelle vitesse. Cette dernière tient compte des bords de la fenêtre.

    # let calc_pv (x,y) (sx,sy) (dx,dy) =
       let nx1 = x+dx     and  ny1 = y + dy
       and nx2 = x+sx+dx  and  ny2 = y+sy+dy
       and ndx = ref dx   and  ndy = ref dy
       in
         ( if (nx1 < 0) || (nx2 >= Graphics.size_x()) then ndx := -dx ) ;
         ( if (ny1 < 0) || (ny2 >= Graphics.size_y()) then ndy := -dy ) ;
         ((x+ !ndx, y+ !ndy), (!ndx, !ndy)) ;;
    val calc_pv :
      int * int -> int * int -> int * int -> (int * int) * (int * int) = <fun>

La fonction `roule_fond` déplace `n` fois le rectangle donné par `pos` et `taille` selon la trajectoire indiquée par sa vitesse `vit` en tenant compte des bords comme décrit par la fonction ci-dessus. La trace du déplacement que l'on obtient sur la figure 5.7 est obtenue par inversion du bitmap correspondant au rectangle déplacé.

    # let  roule_fond pos taille vit n =
       let (x, y) = pos and (sx,sy) = taille in
       let mem = ref (Graphics.get_image x y sx sy) in
       let rec roule_aux  x y vit n =
        if n = 0 then Graphics.moveto x y
        else
         let ((nx,ny),n_vit) = calc_pv (x,y) (sx,sy) vit
         and old_mem = !mem in
          mem := Graphics.get_image nx ny sx sy ;
          Graphics.set_color Graphics.blue;
          Graphics.fill_rect nx ny sx sy;
          Graphics.draw_image (inv_image old_mem) x y;
          roule_aux nx ny n_vit (n-1)
       in roule_aux x y vit n ;;
    val roule_fond : int * int -> int * int -> int * int -> int -> unit = <fun>

Le code suivant correspond aux dessins de la figure 5.7. Le premier est obtenu sur fond uniformément rouge, le second, en déplaçant le rectangle sur l'image de Jussieu.

    # let anim_rect () =
       Graphics.moveto 105 120 ;
       Graphics.set_color Graphics.white;
       Graphics.draw_string "Début" ;
       roule_fond (140,120) (8,8) (8,4) 150 ;
       let (x,y) = Graphics.current_point() in
        Graphics.moveto (x+13) y ;
       Graphics.set_color Graphics.white;
       Graphics.draw_string "Fin" ;;
    val anim_rect : unit -> unit = <fun>
    # anim_rect();;
    - : unit = ()

> Figure 5.7 : Code du déplacement d'un objet

Le problème a été simplifié dans la mesure où il n'y a pas d'intersection entre deux positions successives de l'objet déplacé. Si tel n'est pas le cas, il faut écrire une fonction de calcul de cette intersection qui est plus ou moins compliquée selon la forme de l'objet. Dans le cas présent d'un carré, l'intersection de deux carrés donne un rectangle. C'est celui-ci qu'il faut alors effacer.