Author: Christian.Queinnec@lip6.fr

## Animation

The animation of graphics on a screen reuses techniques of animated drawings. The major part of a drawing does not change, only the animated part must modify the color of its constituent pixels. One of the immediate problems we meet is the speed of animation. It can vary depending on the computational complexity and on the execution speed of the processor. Therefore, to be portable, an application containing animated graphics must take into account the speed of the processor. To get smooth rendering, it is advisable to display the animated object at the new position, followed by the erasure of the old one and taking special care with the intersection of the old and new regions.

##### Moving an object

We simplify the problem of moving an object by choosing objects of a simple shape, namely rectangles. The remaining difficulty is knowing how to redisplay the background of the screen once the object has been moved.
We try to make a rectangle move around in a closed space. The object moves at a certain speed in directions X and Y. When it encounters a border of the graphical window, it bounces back depending on the angle of impact. We assume a situation without overlapping of the new and old positions of the object. The function `calc_pv` computes the new position and the new velocity from an old position `(`x`,`y`)`, the size of the object `(`sx`,`sy`)` and from the old speed `(`dx`,`dy`)`, taking into account the borders of the window.

    # 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>

The function `move_rect` moves the rectangle given by `pos` and `size` `n` times, the trajectory being indicated by its `speed` and by taking into account the borders of the space. The trace of movement which one can see in figure 5.7 is obtained by inversion of the corresponding bitmap of the displaced rectangle.

    # let move_rect pos size speed n =
       let (x, y) = pos and (sx,sy) = size in
       let mem = ref (Graphics.get_image x y sx sy) in
       let rec move_aux x y speed n =
        if n = 0 then Graphics.moveto x y
        else
         let ((nx,ny),n_speed) = calc_pv (x,y) (sx,sy) speed
         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;
          move_aux nx ny n_speed (n-1)
       in move_aux x y speed n;;
    val move_rect : int * int -> int * int -> int * int -> int -> unit = <fun>

The following code corresponds to the drawings in figure 5.7. The first is obtained on a uniformly red background, the second by moving the rectangle across the image of Jussieu.

    # let anim_rect () =
       Graphics.moveto 105 120;
       Graphics.set_color Graphics.white;
       Graphics.draw_string "Start";
       move_rect (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 "End";;
    val anim_rect : unit -> unit = <fun>
    # anim_rect();;
    - : unit = ()

> Figure 5.7: Moving an object.

The problem was simplified, because there was no intersection between two successive positions of the moved object. If this is not the case, it is necessary to write a function that computes this intersection, which can be more or less complicated depending on the form of the object. In the case of a square, the intersection of two squares yields a rectangle. This intersection has to be removed.