• Aucun résultat trouvé

Fonctionnelles et polymorphisme

8.4 Le crayon ´ electronique

Le comportement du « crayon ´electronique » est le suivant :

• Le crayon trace dans une direction qu’on peut faire varier `a la demande et que nous nommerons la « vis´ee » du crayon.

• En avan¸cant le crayon ne laisse pas de trace s’il est lev´e et trace un trait s’il est baiss´e.

Pour g´erer le crayon, il nous faut donc tenir `a jour et faire ´evoluer son ´etat courant : ses coordonn´ees, son angle de vis´ee et le mode de trac´e (crayon lev´e ou non). Cet ´etat est d´ecrit par un type d´efinissant toutes les caract´eristiques du crayon ; c’est donc un type

«et » : un type enregistrement. Les coordonn´ees du crayon et son angle de vis´ee sont des nombres flottants et le statut (lev´e ou non) du crayon est ´evidemment un bool´een. Cela nous conduirait donc `a d´efinir le type ´etat comme

type ´etat =

{ x : float; y : float; vis´ee : float; lev´e : bool };;

et l’´etat courant du crayon comme

let crayon =

{ x = 0.0; y = 0.0; vis´ee = 0.0; lev´e = false };;

Cependant, ce type ne nous permet pas de faire ´evoluer le crayon. Or, nous n’avons qu’un seul crayon dont nous voulons faire varier dynamiquement les caract´eristiques. Pour cela, il faut explicitement d´eclarer au syst`eme Caml que nous d´esirons modifier physiquement les champs de l’enregistrement qui mod´elise le crayon. Comme expliqu´e `

a la section 6.6, il suffit d’indiquer que les champs du type ´etat sont modifiables, en faisant pr´ec´eder les ´etiquettes correspondantes du mot-cl´e mutable lors de la d´efinition du type.

# type ´etat =

{ mutable x : float; mutable y : float;

mutable vis´ee : float; mutable lev´e : bool };; Le type ´etat est d´efini.

Le contrˆoleur de type nous autorisera maintenant `a changer les valeurs des car- act´eristiques d’un objet du type ´etat. La construction d’une valeur d’un enregistrement `

a champs mutables ne diff`ere pas du cas habituel. Nous d´efinissons donc le crayon comme une donn´ee du type ´etat par :

# let crayon = { x = 0.0; y = 0.0; vis´ee = 0.0; lev´e = false };; crayon : ´etat = {x = 0.0; y = 0.0; vis´ee = 0.0; lev´e = false}

Tourner

Faire tourner le crayon consiste `a changer son angle de vis´ee, pour lui im- primer le nouveau cap. On utilise pour cela la modification physique d’un champ d’enregistrement, not´ee par une fl`eche vers la gauche, <-. Ainsi, la fonction qui permet de lever ou de baisser le crayon est simplement :

# let fixe_crayon b = crayon.lev´e <- b;; fixe_crayon : bool -> unit = <fun>

L’angle de vis´ee crayon.vis´ee est exprim´e en radians et suit les conventions du cercle trigonom´etrique des math´ematiques : le z´ero est `a l’est et le crayon tourne dans le sens inverse de celui des aiguilles d’une montre. On rappelle que le cercle trigonom´etrique est le cercle de rayon 1 d’un rep`ere orthonorm´e. Si l’angle θ est rep´er´e par les demi-droites Ox et OM , alors les coordonn´ees (x, y) de M sont respectivement le cosinus et le sinus de l’angle θ.

Le crayon ´electronique 151 Cependant, pour plus de commodit´e, les or-

dres de changement de cap donn´es au crayon seront exprim´es en degr´es. La conversion est simple, puisqu’on a Angle(en radians) = Angle(en degr´es) × π/180. Apr`es avoir nomm´e la valeur π/180 pour faire commod´ement les conversions de degr´es en radians, nous d´efinissons la fonction tourne qui change le cap du crayon. # let pi_sur_180 = let pi = 4.0 *. (atan 1.0) in pi /. 180.0;; pi_sur_180 : float = 0.0174532925199 O 1 1 x y θ M sin (θ) ( | {z } cos (θ)

# let tourne angle =

crayon.vis´ee <- (crayon.vis´ee +. angle *. pi_sur_180);; tourne : float -> unit = <fun>

Avancer

La primitive qui fait avancer le crayon se contente de calculer les d´eplacements du crayon n´ecessaires selon l’axe des abscisses et l’axe des ordonn´ees (dx et dy), `a l’aide des formules trigonom´etriques de base, puis de modifier les coordonn´ees du crayon, et enfin de d´eplacer le crayon, soit en tra¸cant (si le crayon est baiss´e) `a l’aide de la primitive graphique lineto, soit sans tracer de trait (si le crayon est lev´e) en utilisant alors la primitive moveto.

# let avance d =

let dx = d *. cos (crayon.vis´ee) and dy = d *. sin (crayon.vis´ee) in crayon.x <- crayon.x +. dx;

crayon.y <- crayon.y +. dy; if crayon.lev´e

then moveto (round crayon.x) (round crayon.y) else lineto (round crayon.x) (round crayon.y);; avance : float -> unit = <fun>

Utilitaires d’initialisation du crayon

Pour simplifier le travail de l’utilisateur du crayon, le rep`ere du crayon est proche de celui des math´ematiques : l’origine est au centre de l’´ecran graphique. Les coordonn´ees de l’origine sont contenues dans deux constantes zero_x et zero_y qui valent donc respectivement size_x ()/2 et size_y ()/2.

On initialise donc le crayon en fixant ses coordonn´ees au centre de l’´ecran (z´ero_x, z´ero_y), en le faisant pointer vers l’est, en le baissant pour qu’il laisse une trace et en amenant le point courant du graphisme de Caml `a la position actuelle du crayon. Enfin, et c’est le plus difficile, on efface l’´ecran. La fonction obtient cet effet en peignant tout l’´ecran avec la couleur du fond. L’´ecran forme un rectangle de coin inf´erieur gauche (0, 0)

et de coin sup´erieur droit (size_x (), size_y ()). On utilise la fonction pr´ed´efinie fill_rect, qui remplit un rectangle avec la couleur de trac´e courante. Cette couleur est fix´ee par la fonction graphique set_color. Nous avons choisi les couleurs de fond et de trac´e comme sur une feuille de papier, c’est-`a-dire blanc pour le fond (couleur pr´ed´efinie white) et noir pour les points trac´es (couleur pr´ed´efinie black).

# let couleur_du_trac´e = black;; couleur_du_trac´e : color = 0 # let couleur_du_fond = white;; couleur_du_fond : color = 1

# let z´ero_x = float_of_int ((size_x ()) / 2);; z´ero_x : float = 3000.0

# let z´ero_y = float_of_int ((size_y ()) / 2);; z´ero_y : float = 2000.0

# let vide_´ecran () =

set_color couleur_du_fond;

fill_rect 0 0 (size_x ()) (size_y ()); set_color couleur_du_trac´e;

crayon.x <- z´ero_x; crayon.y <- z´ero_y; crayon.vis´ee <- 0.0; crayon.lev´e <- false;

moveto (round crayon.x) (round crayon.y);; vide_´ecran : unit -> unit = <fun>