Dimanche 11 Mai 2008
~ Bibliothèque Graphique ~
Menu
> Accueil

Programmation
> Algorithmes de tri
> Java

Réseaux Telecom
> Logiciel Vigie

Dossiers
> Trajectoire de comètes
> Gestion d'emploi du temps
> Tracking d'internautes
> Référencement
> Open Office
> Multi-agents dans les EIAH

Divers
> Album Photo
> Citations
> Recettes
> Bibliothèque
> Logiciels
> Mini-Annuaire

A propos
> Mon CV
> Me contacter
Recherche
Google
Sur ce site
Sur le web
Annonces
Accueil > Trajectoire de comètes > Bibliothèque Graphique
  1. Introduction

    1. Position du problème

      Pour visualiser des variations de position d’un point au cours du temps, seule la représentation graphique est réellement pertinente et parlante, or la bibliothèque graphique de caml, bien qu’exhaustive, ne permet pas, comme le fait Maple de tracer directement des courbes. Nous avons donc été amenés à considérer le tracé de courbes ou de séries de points en Caml.

    2. Problème du graphisme en Caml

      Lors du tracé de courbes, nous travaillons généralement sur des flottants. Or la fenêtre graphique de Caml n’accepte que des points dont les coordonnées sont des entiers!! De plus, l’origine du repère graphique de Caml est fixe, il serait pourtant plus aisé de pouvoir définir sa propre représentation d’un repère. Cette caractéristique surprend, surtout lorsqu’on a été habitué à des logiciels tel que Maple où la construction graphique est quasi-automatique. En Caml, tout - ou presque - est à redéfinir.

  2. Outils de base

    1. Fonctionnement de Caml

      1. Ouvrir la fenêtre graphique

        #open "graphics";;

        Ouvre la bibliothéque graphique de Caml

        open_graph "highest";;

        #- : unit = ()

        Permet d’ouvrir la fenêtre graphique de Caml au maximum.

        cleargraph ( ) ;;

        #- : unit = ()

        Permet d’effacer l’ensemble de la fenêtre graphique.

      2. Outils de base

        En Caml, il existe plusieurs types d’ordres et de fonctions.
        "size_x()" renvoie la taille de l’écran en abscisse
        "size_y()" renvoie la taille de l’écran en ordonnée
        "moveto x y" place le point courant en (x,y)
        "lineto x y" trace une ligne entre le point courant et le point de coordonnées (x,y) ; Ce dernier devient à son tour le point courant.

        Rappel : Dans toutes ces définitions, x et y sont des entiers

      3. La fenêtre graphique Caml

        Les points de la fenêtre graphique sont des pixels.
        Le nombre de pixels sur la fenêtre graphique en abscisse est size_x, en ordonnée size_y.

        Remarque : Travaillant sur des entiers, le nombre maximal de pixels est (size_x)*(size_y).

        L’origine (0,0) du repère graphique est situé en bas, à gauche de l’écran.

    2. Redéfinition des outils de base

      Notre utilisation des flottants n’est pas compatible avec l’utilisation des entiers par Caml. Afin de rendre exploitable pour Caml une fonction graphique définie exclusivement sur des flottants, nous allons donc être amenés à jongler entre entiers et flottants.

      1. Du repère virtuel au repère écran

        On appellera repére virtuel un repére (O,i,j) du plan R².
        Le repère écran est le repère associé à la fenêtre graphique. Son origine se trouve dans le coin en bas à gauche de l’écran. Les coordonnées (qui sont des couples d’entiers ) dans ce repère représentent des pixels. Le but est de fournir une représentation d’un ensemble de points du plan R² à l’écran. Il faut donc établir une relation entre les coordonnées d’un point dans le repère virtuel et ses coordonnées dans le repère de la fenêtre graphique. Cette relation ne peut être une bijection ( R² est indénombrable alors que l’ensemble des pixels de l’écran l’est). Il s’agit en fait d’une surjection: à un pixel correspondent plusieurs points virtuels.

      2. Remarques préliminaires

        L’origine du repère écran se trouve en (0,0) en bas à gauche de l’écran graphique. On place l’origine du repère virtuel où on désire. On peut en effet travailler sur des flottants négatifs. Il convient donc de définir un nouveau type repère qui permettra de référencer l’origine (0,0) comme on le souhaitera.
        Pour passer des flottants aux entiers, nous allons être amenés à faire des arrondis et des approximations. Une partie de l’information, ou tout au moins de la précision, risque d’être perdue.
        Certains problèmes peuvent donc apparaître lorsque l’échelle des valeurs que l’on a à traiter est très petite. Ces problèmes sont facilement évitables (il suffit de multiplier les valeurs par une constante afin d’accroître l’échelle des valeurs), mais l’utilisateur doit être prévenu et être conscient de ce problème.

      3. Définition des types

        Il faut redéfinir le type point écran.

        type p_écran={X:int;Y:int}

        #Le type p_ecran est défini.


        Il est donc homogène à des entiers.
        Par convention, tout ce qui concerne l’écran sera utilisé avec des lettres majuscules (X;Y).

        Ensuite on définit le type point repère (qui correspond au point du repère virtuel de R²).

        type p_repere={x :float ;y :float} ;;

        #Le type p_repere est défini.


        Le type point_repere est défini à l’aide de flottants (ce sont des points virtuels).
        Par convention, tout ce qui concerne le repère virtuel sera noté à l’aide de lettres minuscules (x,y).

        Enfin on définit un type repère, contenant les paramètres de la représentation du repère(O,i,j) à l’écran.

        type repere={O:point_écran ;I:int;J:int};;

        #Le type repere est défini. O : point écran correspondant au centre du repére virtuel à l’ écran
        I : vecteur unitaire des abscisses
        J : vecteur unitaire des ordonnées

      4. Autres fonctions de base

        let rep_to_ecr pt_rep rep =
        {X=(rep.O.X)+( int_of_float((pt_rep.x)*.float_of_int(rep.I)) );
        Y=(rep.O.Y)+( int_of_float((pt_rep.y)*.float_of_int(rep.J)) )};;

        #rep_to_ecr : p_repere -> repere -> p_ecran = <fun>

        rep_to_écr prend en argument un point repère et un repère et renvoie un point écran. Il permet de placer le repère dans le repère écran et renvoie donc le point écran correspondant. Il fait en réalité la correspondance entre R² et l’écran.

        Remarque : Il n’y a toujours pas d’application graphique dans cette fonction. Elle permet uniquement de récupérer un point écran en fonction du repère dans lequel on veut exprimer le point repère.

  3. Bibliothèque graphique en type point_écran

    1. Redéfinition de moveto et lineto

      Moveto et lineto sont préexistants en Caml. Il suffit de redéfinir en type point_écran.
      Ainsi:

      • moveto <=> move_écran
      • lineto <=> line_écran

      let move_ecr pt_ecr = moveto (pt_ecr.X) (pt_ecr.Y) ;;

      #move_ecr : p_ecran -> unit = <fun>

      let line_ecr pt_ecr = lineto (pt_ecr.X) (pt_ecr.Y) ;;

      #line_ecr : p_ecran -> unit = <fun>

      move_ecr et line_ecr prennent en argument un point écran. Move_ecr déplace le curseur graphique au point_écran pris en argument.
      Line_ecr trace un segment entre le point_écran courant et le point_écran reçu en argument.

    2. Tracé d’un segment

      let segment_ecr pt_ecr1 pt_ecr2 =
      moveto (pt_ecr1.X) (pt_ecr1.Y);
      lineto (pt_ecr2.X) (pt_ecr2.Y);;

      #segment_ecr : p_ecran -> p_ecran -> unit = <fun>

      Segment_ecr prend en argument deux points écran et trace le segment qui les lie.

    3. Tracé d’un point

      let point_ecr pt_ecr =
      segment_ecr {X=((pt_ecr.X)-2) ; Y=pt_ecr.Y} {X=((pt_ecr.X)+2) ; Y=(pt_ecr.Y)} ;
      segment_ecr {X=(pt_ecr.X) ; Y=((pt_ecr.Y)+2)} {X=(pt_ecr.X) ; Y=((pt_ecr.Y)-2)} ;;

      #point_ecr : p_ecran -> unit = <fun>

      Un point, compte-tenu de la résolution graphique de l’écran de l’ordinateur, ne serait pas très visible. Par conséquent, un point sera représenté par une petite croix.
      Point_écr prend en argument un point écran et trace le point à l’écran sous forme d’une petite croix.

  4. Bibliothèque graphique en type point_repère

    let move_rep pt_rep rep = move_ecr (rep_to_ecr pt_rep rep) ;;

    #move_rep : p_repere -> repere -> unit = <fun>

    Move_rep prend en argument un point repère et un repère et place le point écran correspondant sous forme de point courant (pas d’affichage à l’écran).

    let line_rep pt_rep rep = line_ecr (rep_to_ecr pt_rep rep) ;;

    #line_rep : p_repere -> repere -> unit = <fun>

    Line_rep prend en argument un point repère et trace le segment entre le point repère courant et le point repère pris en argument.

    let point_rep pt_rep rep = point_ecr (rep_to_ecr pt_rep rep) ;;

    #point_rep : p_repere -> repere -> unit = <fun>

    Point_rep prend en argument un point repère et un repère, et affiche le point correspondant à l’écran sous forme d’une petite croix.

    let segment_rep pt_rep1 pt_rep2 rep =
            move_rep pt_rep1 rep;
            line_rep pt_rep2 rep;;

    #segment_rep : p_repere -> p_repere -> repere -> unit = <fun>

    Segment_rep prend en argument deux points repère et un repère, et trace le segment correspondant à l’écran.

  5. Définition de la taille des fenêtres et tracé du repère

    1. définition de la taille des fenêtres

      let fenetre xmin xmax ymin ymax=
          let II=int_of_float((float_of_int(size_x()))/.(xmax-.xmin))
          and JJ=int_of_float((float_of_int(size_y()))/.(ymax-.ymin)) in
          {O={X=int_of_float((-.xmin)*.float_of_int(II));Y=int_of_float((-.ymin)*.float_of_int(JJ))} ; I=II ; J=JJ};;

      #fenetre : float -> float -> float -> float -> repere = <fun>

      fenetre reçoit en argument quatre flottants qui correspondent aux valeurs extrêmes que prennent x et y, soit dans l’ordre xmin, xmax, ymin, ymax.
      La fenêtre est ainsi centrée et renvoie un repère qui lui correspond.(Le repére renvoyé par fenetre est orthonormal, mais pas forcément orthonormé.)

      let vision centre largeur=
         let (a,b)=centre and hauteur=(largeur*.(float_of_int (size_y()))/.(float_of_int (size_x()))) in
                       fenetre (a-.(largeur/.2.)) (a+.(largeur/.2.)) (b-.(hauteur/.2.)) (b+.(hauteur/.2.));;
          
      #vision : float * float -> float -> repere = <fun>

      vision quant à lui renvoi un repère orthonormé. On ne peut donc plus choisir la hauteur de la fenêtre qui est définie automatiquement. vision reçoit donc en argument la position à l’écran du centre du repère (il s’agit d’un couple de flottants) ainsi que la largeur de l’écran.
      Si l’on choisit par exemple de positionner le centre du repère au centre de l’écran, on prend (0. ,0.) pour centre du repère.

    2. Tracé du repère

      let trace_rep rep n couleur=
          set_color couleur;
          moveto 0 (rep.O.Y);
          lineto (size_x()) (rep.O.Y);
          moveto (rep.O.X) 0;
          lineto (rep.O.X) (size_y());
          
          let XM=int_of_float( float_of_int( abs(size_x()-(rep.O.X)) ) /. float_of_int(rep.I) )
          and Xm=int_of_float( float_of_int( abs(rep.O.X) ) /. float_of_int(rep.I) )
          and YM=int_of_float( float_of_int( abs(size_y()-(rep.O.Y)) ) /. float_of_int(rep.J) )
          and Ym=int_of_float( float_of_int( abs(rep.O.Y) ) /. float_of_int(rep.J) ) in
          
          let sX= float_of_int(rep.I)/.float_of_int(n)
          and sY= float_of_int(rep.J)/.float_of_int(n) in

          move_ecr { X=((rep.O.X)+(rep.I)) ; Y=(rep.O.Y) } ;
          line_ecr { X=(((rep.O.X)+(rep.I))-7); Y=((rep.O.Y)+7) };
          move_ecr { X=((rep.O.X)+(rep.I)) ; Y=(rep.O.Y) } ;
          line_ecr { X=(((rep.O.X)+(rep.I))-7); Y=((rep.O.Y)-7) };
          
          move_ecr { X=(rep.O.X) ; Y=(rep.O.Y)+(rep.J) } ;
          line_ecr { X=((rep.O.X)-7); Y=(rep.O.Y)+(rep.J)-7 };
          move_ecr { X=(rep.O.X) ; Y=(rep.O.Y)+(rep.J) } ;
          line_ecr { X=((rep.O.X)+7); Y=(rep.O.Y)+(rep.J)-7 };

          for i=0 to (XM+1) do
            move_ecr { X= ((rep.O.X)+i*(rep.I)) ; Y= ((rep.O.Y)+5) };
            line_ecr { X= ((rep.O.X)+i*(rep.I)) ; Y= ((rep.O.Y)-5) };
           for j=1 to (n) do
      move_ecr { X= ((rep.O.X)+i*(rep.I)) + int_of_float(float_of_int(j)*.sX) ; Y=((rep.O.Y)+2) };
      line_ecr { X= ((rep.O.X)+i*(rep.I)) + int_of_float(float_of_int(j)*.sX) ; Y=((rep.O.Y)-2) };
                         done;
          done;
          
          for i=0 downto (-Xm-1) do
            move_ecr { X= ((rep.O.X)+i*(rep.I)) ; Y= ((rep.O.Y)+5) };
            line_ecr { X= ((rep.O.X)+i*(rep.I)) ; Y= ((rep.O.Y)-5) };
           for j=(-1) downto (-n) do
      move_ecr { X= ((rep.O.X)+i*(rep.I)) + int_of_float(float_of_int(j)*.sX) ; Y=((rep.O.Y)+2) };
      line_ecr { X= ((rep.O.X)+i*(rep.I)) + int_of_float(float_of_int(j)*.sX) ; Y=((rep.O.Y)-2) }
                         done;
          done;

          for i=0 to (YM+1) do
            move_ecr { X= ((rep.O.X)+5) ; Y= ((rep.O.Y)+i*(rep.J)) };
            line_ecr { X= ((rep.O.X)-5) ; Y= ((rep.O.Y)+i*(rep.J)) };
          for j=1 to (n) do
      move_ecr { X=((rep.O.X)+2) ; Y= ((rep.O.Y)+i*(rep.J)) + int_of_float(float_of_int(j)*.sY) };
      line_ecr { X=((rep.O.X)-2) ; Y= ((rep.O.Y)+i*(rep.J)) + int_of_float(float_of_int(j)*.sY) };
      done;
          done;
              
          for i=0 downto (-Ym-1) do
            move_ecr { X=((rep.O.X)+5) ; Y=((rep.O.Y)+i*(rep.J)) };
            line_ecr { X=((rep.O.X)-5) ; Y=((rep.O.Y)+i*(rep.J)) };
          for j=(-1) downto (-n) do
      move_ecr { X=((rep.O.X)+2) ; Y= ((rep.O.Y)+i*(rep.J)) + int_of_float(float_of_int(j)*.sY) };
      line_ecr { X=((rep.O.X)-2) ; Y= ((rep.O.Y)+i*(rep.J)) + int_of_float(float_of_int(j)*.sY) };
      done;
          done;
      set_color black;;

      #trace_rep : repere -> int -> color -> unit = <fun>

      Trace_rep reçoit en argument un repère, le nombre de subdivisions qui doivent être tracées sur les axes ainsi que la couleur du tracé. Cette fonction trace les axes et ses graduations en fonctions des vecteurs unitaires ( I et J définis dans le type repère).

      let axes rep =moveto 0 (rep.O.Y);
              lineto (size_x()) (rep.O.Y);
              moveto (rep.O.X) 0;
              lineto (rep.O.X) (size_y());;

      #axes : repere -> unit = <fun>

      axes reçoit en argument un repère et trace les axes (sans les graduations).

  6. Tracé de fonctions

    1. Généralités

      Le tracé de fonctions du type y=f(x) se fait à l’aide de la fonction trace_fun.

      let trace_fun f xmin xmax n rep=
               let h=(xmax-.xmin)/.(float_of_int n) in
               move_rep { x=xmin ; y= (f xmin) } rep;
                   for i=1 to n-1 do
                 line_rep { x=xmin+.((float_of_int i)*.h) ; y= f (xmin+.((float_of_int i)*.h)) } rep;
                   move_rep { x=xmin+.((float_of_int i)*.h) ; y=f (xmin+.((float_of_int i)*.h))} rep;
                 done;;

      #trace_fun : (float -> float) -> float -> float -> int -> repere -> unit = <fun>

      Elle reçoit en argument :

      • une fonction de type (float->float)
      • la valeur minimale sur l’axe des abscisses (flottant)
      • la valeur maximale sur l’axe des abscisses (flottant)
      • le nombre de points à calculer n (entier)
      • un repère.
      Cette fonction renvoie le tracé de la fonction reçue en argument, entre les valeurs xmin et xmax (elles aussi reçues en argument), et dans le repère choisi.

      Remarque : Plus n est grand, plus la précision à l’écran de la fonction sera élevée.
      Toutefois plus n est grand, plus le temps de réponse est élevé. Une résolution correcte existe pour 500 points (temps de réponse instantané). Pour 5000 points, bonne résolution, temps de réponse 3-4 s. Pour 50000 points, excellente résolution, temps de réponse 15 s. Lorsque des points ne sont pas définis (exemple: x->sqrt(x) pour x<0), l’opérateur graphique en Caml trace une droite grisée. Il n’y a donc pas besoin d’une exception lorsque des points ne sont pas définis.

      Exemple :

      Tracé de et de x².

      Procédure du tracé :

      let rep=fenetre (-.10.) (10.) (-.10.) 10.;; (* On définit un repère. *)
      rep : repere = {O = {X = 400; Y = 300}; I = 40; J = 30}
      let f1 x=sqrt x;; (* On définit deux fonctions. *)
      let f2 x=x*.x;;
      f1 : float -> float = <fun>
      f2 : float -> float = <fun>
      clear_graph();; (* Efface la fenêtre graphique. *)
      - : unit = ()
      trace_rep rep 5 blue ;; (* Trace le repère (avec les graduations et 5 subdivisions) *)
      - : unit = ()
      trace_fun f1 (-.10.) (10.) 5000 rep ;; (* Trace la première fonction (en noir). *)
      - : unit = ()
      set_color red;;
      trace_fun f2 (-.10.) (10.) 5000 rep ;; (* Trace la deuxième fonction (en rouge). *)
      #- : unit = ()
      #- : unit = ()

    2. Outils pour tracer des graphiques à partir de listes de points

      1. En prédéfinissant un repère

        On peut utiliser les fonctions nuage_points et polygone.

        let nuage_pts pliste rep =
        let pliste2 = ref (pliste) in
        while (!pliste2)<>[] do
                point_rep (hd !pliste2) rep;
                pliste2 := tl (!pliste2);
        done;;

        #nuage_pts : p_repere list -> repere -> unit = <fun>

        Nuage_pts pose un point pour chaque point de la liste. Cela peut être intéressant si l’on veut étudier, en même temps que la trajectoire, la cinématique (vitesse) qui lui est associée (plus les points sont rapprochés, plus la vitesse est faible, et réciproquement).

        let polygone pliste rep =
        let pliste2 = ref (pliste) in
        move_rep (hd !pliste2) rep;
        pliste2 := (tl !pliste2);
        while (!pliste2)<>[] do
                line_rep (hd !pliste2) rep;
                pliste2 := tl (!pliste2);
        done;set_color black;;

        #polygone : p_repere list -> repere -> unit = <fun>

        Polygone trace les droites entre les points. On a ainsi la trajectoire « pleine » du point. On perd cependant en précision (ce qui est toutefois négligeable si le nombre de points est assez élevé).

        Remarque : Ces deux fonctions reçoivent en argument une liste de points repère et un repère.

        Pour tracer une fonction à l’aide de ces deux applications, on crée une liste de point à l’aide de la fonction l_pts_fun qui reçoit en argument :

        • une fonction (qui renvoie des flottants)
        • la valeur minimale de x
        • la valeur maximale de x
        • le nombre de points qu’on veut calculer (plus le nombre est élevé, plus la courbe est précise).

        let l_pts_fun f xmin xmax n =
                 let liste=ref([]) and h=(xmax-.xmin)/.(float_of_int n) in
                 for i=n-1 downto 1 do
                   liste:={ x=xmin+.((float_of_int i)*.h) ; y= f (xmin+.((float_of_int i)*.h)) }:: (!liste);
                   done;
        (!liste);;

        #l_pts_fun : (float -> float) -> float -> float -> int -> p_repere list =<fun>

        de même pour les courbes paramétrées on utilise l_pts_para où f et g sont deux fonctions de t.

        let l_pts_para (f,g) tmin tmax n=
                let liste=ref([]) and h=((tmax-.tmin)/.(float_of_int(n))) in
                for i=n-1 downto 1 do
                   liste:={x=(f(tmin+.((float_of_int i)*.h)));y=(g(tmin+.((float_of_int i)*.h)))}::(!liste);
                done;
        (!liste);;

        #l_pts_para : (float -> float) * (float -> float) -> float -> float -> int -> p_repere list = <fun>

      2. Sans prédéfinir de repère

        Le repère se définit automatiquement à partir de la liste de points (on utilise pour cela les fonctions intermédiaires minx, miny, maxx, maxy qui recherchent les valeurs minimales et maximales de x et y dans une liste de points, et fenêtre).

        let minx pliste =
        let pliste2 = ref (pliste) and mx =ref(((hd (pliste)).x)) in
        while (!pliste2)<>[] do
                if ((hd (!pliste2)).x) <= (!mx) then mx:=((hd (!pliste2)).x);
                pliste2 := tl (!pliste2);
        done;
        (!mx);;

        #minx : p_repere list -> float = <fun>

        let miny pliste =
        let pliste2 = ref (pliste) and my =ref(((hd (pliste)).y)) in
        while (!pliste2)<>[] do
                if ((hd (!pliste2)).y) <= (!my) then my:=((hd (!pliste2)).y);
                pliste2 := tl (!pliste2);
        done;
        (!my);;

        #miny : p_repere list -> float = <fun>

        let maxx pliste =
        let pliste2 = ref (pliste) and Mx =ref(((hd (pliste)).x)) in
        while (!pliste2)<>[] do
                if ((hd (!pliste2)).x) >= (!Mx) then Mx:=((hd (!pliste2)).x);
                pliste2 := tl (!pliste2);
        done;
        (!Mx);;

        #maxx : p_repere list -> float = <fun>

        let maxy pliste =
        let pliste2 = ref (pliste) and My =ref(((hd (pliste)).y)) in
        while (!pliste2)<>[] do
                if ((hd (!pliste2)).y) >= (!My) then My:=((hd (!pliste2)).y);
                pliste2 := tl (!pliste2);
        done;
        (!My);;

        #maxy : p_repere list -> float = <fun>

        Remarque : ces fonctions vont permettre de cadrer automatiquement la fenêtre graphique.
        On redéfinit ainsi les fonctions plot_polygones et plot_nuage qui ne reçoivent plus en argument qu’une liste de points et qui globalement, effectuent les mêmes opérations nuage_pts et polygone tout en choisissant eux même leur repére.

        let plot_nuage pliste=
        let xmin=(minx pliste)
        and xMax=(maxx pliste)
        and ymin=(miny pliste)
        and yMax=(maxy pliste) in
        let xm=xmin-.(0.1)*.(xMax-.xmin)
        and xM=xMax+.(0.1)*.(xMax-.xmin)
        and ym=ymin-.(0.1)*.(yMax-.ymin)
        and yM=yMax+.(0.1)*.(yMax-.ymin) in
        let (rx,ry)=
        if (yM-.ym)>(100.*.(xM-.xm)) then
                ((0.),(( (float_of_int(size_y())) *. (xM-.xm) /.(float_of_int(size_x())) )+.(ym-.yM) )/.(2.))
        else
        if (xM-.xm)>(100.*.(yM-.ym)) then
                ((( (float_of_int(size_x())) *. (yM-.ym) /.(float_of_int(size_y())) )+.(xm-.xM) )/.(2.),0.)
        else
        if ((float_of_int(size_x())) *. (yM-.ym) /.(float_of_int(size_y())))>=xM-.xm
                then ((( (float_of_int(size_x())) *. (yM-.ym) /.(float_of_int(size_y())) )+.(xm-.xM) )/.(2.),0.)
                else ((0.),(( (float_of_int(size_y())) *. (xM-.xm) /.(float_of_int(size_x())) )+.(ym-.yM) )/.(2.)); in
        let rep=fenetre (xm-.rx) (xM+.rx) (ym-.ry) (yM+.ry) in
        trace_rep rep 10 black;
        nuage_pts pliste rep;;

        #plot_nuage : p_repere list -> unit = <fun>

        let plot_polygone pliste=
        let xmin=(minx pliste)
        and xMax=(maxx pliste)
        and ymin=(miny pliste)
        and yMax=(maxy pliste) in
        let xm=xmin-.(0.1)*.(xMax-.xmin)
        and xM=xMax+.(0.1)*.(xMax-.xmin)
        and ym=ymin-.(0.1)*.(yMax-.ymin)
        and yM=yMax+.(0.1)*.(yMax-.ymin) in
        let (rx,ry)=
        if (yM-.ym)>(100.*.(xM-.xm)) then
                ((0.),(( (float_of_int(size_y())) *. (xM-.xm) /.(float_of_int(size_x())) )+.(ym-.yM) )/.(2.))
        else
        if (xM-.xm)>(100.*.(yM-.ym)) then
                ((( (float_of_int(size_x())) *. (yM-.ym) /.(float_of_int(size_y())) )+.(xm-.xM) )/.(2.),0.)
        else
        if ((float_of_int(size_x())) *. (yM-.ym) /.(float_of_int(size_y())))>=xM-.xm
                then ((( (float_of_int(size_x())) *. (yM-.ym) /.(float_of_int(size_y())) )+.(xm-.xM) )/.(2.),0.)
                else ((0.),(( (float_of_int(size_y())) *. (xM-.xm) /.(float_of_int(size_x())) )+.(ym-.yM) )/.(2.)); in
        let rep=fenetre (xm-.rx) (xM+.rx) (ym-.ry) (yM+.ry) in
        trace_rep rep 10 black;
        polygone pliste rep;;

        #plot_polygone : p_repere list -> unit = <fun>

        Attention: Ces deux fonctions peuvent donner des résultats peu agréables, voir pas de résultat du tout, si la liste de point n’est pas très pertinente, c'est à dire s’il y a certaines disproportions (de grandes valeurs en côtoient des petites) ou si certains points ne sont carrément pas définis. (ex : tracer une exponentielle entre (-10) et 10 donne un résultat extrêmement mauvais alors qu’au contraire, entre (-10) et 2 le résultat et assez agréable). Par conséquent, les fonctions qui vont suivre ne pourront être utilisées si la fonction n’est pas définie (comme pour quand x<0 ). Quand le nombre de valeurs indéfinies est fini ont peu cependant tracer la courbe (comme pour tan x où les asymptotes seront alors visualisées (cf exemples)), si l’on a pas de résultat, on peu essayer de faire varier le nombre de valeurs calculées car il se peut que l’on soit tombé précisément sur une indéterminée (cas extrêmement rare).

    3. Tracé des différents types de fonctions.

      Pour les fonctions classiques, on utilise plot_fun qui reçoit en argument :

      • une fonction
      • la valeur minimale de x
      • la valeur maximale de x
      • le nombre de points à calculer.

      let plot_fun f xm xM n=
         let pliste= (l_pts_fun f xm xM n) in
      plot_polygone pliste;;

      #plot_fun : (float -> float) -> float -> float -> int -> unit = <fun>

      Pour les fonctions trigonométriques, on utilise plot_para qui reçoit en argument :

      • un couple de fonctions (x(t),y(t))
      • la valeur minimale de t
      • la valeur maximale de t
      • le nombre de points à calculer

      let plot_para (f,g) tmin tmax n=
      let pliste= (l_pts_para (f,g) tmin tmax n) in
      plot_polygone pliste;;

      plot_para : (float -> float) * (float -> float) -> float -> float -> int -> unit = <fun>

      Pour les fonctions polaires, on utilise plot_polaire qui reçoit en argument :

      • une fonction r(t)
      • la valeur minimale de t
      • la valeur maximale de t
      • le nombre de points à calculer

      let plot_polaire f amin amax n=
      let pliste= (l_pts_para ((fun x->cos(x)*.f(x)),(fun x->sin(x)*.f(x))) amin amax n) in
      plot_polygone pliste;;

      plot_polaire : (float -> float) -> float -> float -> int -> unit = <fun>

  7. Outils supplémentaires

    Les listes de points peuvent nous être données sans être typées en type point_repère. Trois routines existent donc pour effectuer la conversion.

    let convert_vect t =
    let n=vect_length t and r=ref[ ] in
              for i=(n-1) downto 0 do
              let (m,n)=t.(i) in r :=[{x=m ;y=n}]@( !r)
               done ;
           ( !r);;

    #convert_vect : (float * float) vect -> p_repere list = <fun>

    convert_vect convertit un tableau de coordonnées (x,y) en une liste de point_repère.

    let rec convert_list l = match l with
       |[ ] -> [ ]
       |[(a,b)] -> [{x=a ;y=b}]
       |r::ll -> (convert_list [r])@(convert_list ll);;

    #convert_list : (float * float) list -> p_repere list = <fun>

    convert_list convertit une liste de coordonnées (x,y) en une liste de point_repère.

    let rec projection l a b = match l with
         |[ ] -> []
         |[(t)] -> [{x=t.(a-1);y=t.(b-1)}]
         |r::ll -> (projection [r] a b)@(projection ll a b);;

    #projection : float vect list -> int -> int -> p_repere list = <fun>

    projection reçoit en argument une liste de liste de coordonnées et deux entiers qui correspondent aux deux positions des coordonnées qu’on prend dans chaque liste.

  8. Quelques exemples

    Nous allons tracer :
    a) Deux courbes « classiques » : y=cos x et y=tan x
    b) Une courbe paramétrée : x(t)=cos 3t ; y(t)=sin 2t (courbe de Lissajous)
    c) Une courbe polaire : r =ln t

    Procédures :(précédées de clear_graph() pour effacer les précédents graphiques)

    a-1)

    plot_fun (fun x->5.*.cos x) (-.8.) (8.) 5000;;
    #- : unit = ()



    a-2)

    plot_fun (fun x->tan x) (-.8.) (8.) 5000;;
    #- : unit = ()



    b)

    plot_para ((fun t->cos (3.*.t)),(fun t->sin (2.*.t))) (0.) (6.29) 5000 ;;
    #- : unit = ()



    c)

    plot_polaire (fun t->log t) (0.) (30.) 5000;;
    #- : unit = ()




Accueil > Trajectoire de comètes > Bibliothèque Graphique