• Aucun résultat trouvé

Exemple : module de simulation d’écran graphique

1.3 RÉCURSIVITÉ DES OBJETS

1.4.2 Exemple : module de simulation d’écran graphique

On veut faire une simulation de dessins en mode graphique. Pour cela, on définit un module qui a l’interface suivante :

• void initialiserEcran (int nl, int nc) ; initialise un espace mémoire de simulation de l’écran de nl lignes sur nc colonnes numérotées de 0 à nl-1, et de 0 à nc-1 ; le crayon de couleur noire est positionné au milieu de l’écran.

• void crayonEn (int nl, int nc) ; positionne le crayon en (nl, nc).

• void couleurCrayon (int c) ; définit la couleur du crayon (de 0 à 15 par exemple).

• void ecrirePixel (int nl, int nc) ; écrit au point (nl, nc) un pixel de la couleur du crayon (en fait écrit un caractère dépendant de la couleur du pixel).

• void avancer (int d, int n) ; avance de n pixels dans la direction d ; 4 directions sont retenues : gauche, haut, droite, bas.

module - définitions des fonctions dont

le prototype a été donné en (A) ci-dessus - définitions de fonctions locales au module

02Chap_01 Page 25 Samedi, 17. janvier 2004 10:36 10

26 1 Récursivité, pointeurs, modules

• void rectangle (int xcsg, int ycsg, int xcid, int ycid) ; trace un rectangle de la couleur du crayon, de cordonnées (xcsg, ycsg) pour le coin supérieur gauche et (xcid, ycid) pour le coin inférieur droit.

• void ecrireMessage (int nl, int nc, char* message) ; écrit message en (nl, nc).

• void afficherEcran() ; affiche l’écran.

• void effacerEcran() ; efface l’écran.

• void detruireEcran() ; détruit l’écran (libère l’espace alloué).

• void sauverEcran (char* nom) ; sauve l’écran dans le fichier nom.

La partie interface définit également les couleurs utilisables (NOIR, BLANC) et les directions de déplacement du crayon (GAUCHE, HAUT, DROITE, BAS).

Aucune variable ne figure dans la partie interface. L’utilisateur ne sait pas comment son écran est mémorisé. L’écran est, pour lui, un type abstrait de données (TAD).

1.4.2.a Le fichier d’en-tête de l’écran graphique

Le fichier d’en-tête décrit les objets visibles pour les utilisateurs du module (constantes NOIR et BLANC, directions de déplacement, prototypes des fonctions).

/* ecran.h fichier d'en-tête pour le module ecran.cpp */

#ifndef ECRAN_H

#define ECRAN_H

#define NOIR 0

#define BLANC 15

#define GAUCHE 1

#define HAUT 2

#define DROITE 3

#define BAS 4

void initialiserEcran (int nl, int nc);

void crayonEn (int nl, int nc);

void couleurCrayon (int c);

void ecrirePixel (int nl, int nc);

void avancer (int d, int lg);

void rectangle (int xcsg, int ycsg, int xcid, int ycid);

void ecrireMessage (int nl, int nc, char* message);

void afficherEcran ();

void effacerEcran ();

void detruireEcran ();

void sauverEcran (char* nom);

#endif

1.4.2.b Le module écran graphique

Comme l’indique la Figure 16, ecran.cpp contient les données locales, et les défini-tions des foncdéfini-tions déclarées dans la partie interface. Les données globales (externes aux fonctions) étant static ne peuvent être référencées de l’extérieur du module. Ces données sont locales au fichier ecran.cpp.

02Chap_01 Page 26 Samedi, 17. janvier 2004 10:36 10

1.4 • Modules 27

© Dunod – La photocopie non autorisée est un délit.

/* ecran.cpp simulation d'écran graphique */

#include <stdio.h> // printf, FILE, fopen, fprintf

#include <stdlib.h> // malloc, free, exit

#include <string.h> // strlen

#include "ecran.h"

// données locales au fichier ecran.cpp, // inaccessibles pour l'utilisateur du module.

// static = locales au fichier pour les variables externes aux fonctions static char* ecran; // pointeur sur le début de l'écran

// l'écran est un tableau de caractères ecran alloué dynamiquement // de nl lignes sur nc colonnes et mis à blanc

void initialiserEcran (int nl, int nc) { nbLig = nl;

nbCol = nc;

ecran = (char*) malloc (nbLig * nbCol * sizeof(char));

effacerEcran ();

}

// le crayon est mis en (nl, nc) void crayonEn (int nl, int nc) { nlc = nl;

ncc = nc;

}

// la couleur du dessin est c void couleurCrayon (int c) { if (c>15) c = c % 16;

couleur = c;

}

// écrire un caractère en fonction de la couleur en (nl, nc) void ecrirePixel (int nl, int nc) {

static char* tabCoul = "*123456789ABCDE.";

if ( (nl>=0) && (nl<nbLig) && (nc>=0) && (nc<nbCol) ) ecran [nl*nbCol+nc] = tabCoul [couleur];

}

// avancer dans la direction d de lg pixels void avancer (int d, int n) {

02Chap_01 Page 27 Samedi, 17. janvier 2004 10:36 10

28 1 Récursivité, pointeurs, modules

// tracer un rectangle défini par 2 points csg et cid void rectangle (int xcsg, int ycsg, int xcid, int ycid) { int longueur = xcid-xcsg+1;

int largeur = ycid-ycsg+1;

crayonEn (ycsg, xcsg);

avancer (BAS, largeur);

avancer (DROITE, longueur);

avancer (HAUT, largeur);

avancer (GAUCHE, longueur);

}

// écrire un message à partir de (nl, nc)

void ecrireMessage (int nl, int nc, char* message) { for (int i=0; i<strlen(message); i++) {

if ( (nl>=0) && (nl<nbLig) && (nc>=0) && (nc<nbCol) ) {

for (int j=0; j<nbCol; j++) printf ("%c", ecran [i*nbCol+j]);

printf ("\n");

}

printf ("\n");

}

// mettre l'écran à blanc void effacerEcran () {

// rendre au système d'exploitation, l'espace alloué par // malloc() dans initialiserEcran()

void detruireEcran () { free (ecran);

}

// sauver l'écran dans le fichier nomFS void sauverEcran (char* nomFS) {

FILE* fs = fopen (nomFS, "w");

if (fs==NULL) { perror ("sauverEcran"); exit (1); };

for (int i=0; i<nbLig; i++) {

for (int j=0; j<nbCol; j++) fprintf (fs, "%c", ecran [i*nbCol+j]);

fprintf (fs, "\n");

}

fprintf (fs, "\n");

fclose (fs);

}

02Chap_01 Page 28 Samedi, 17. janvier 2004 10:36 10

1.4 • Modules 29

© Dunod – La photocopie non autorisée est un délit.

1.4.2.c Le programme d’application de l’écran graphique

Ce programme principal utilise le module écran pour dessiner une maison. Bien sûr, il faudrait augmenter la résolution pour avoir un dessin plus fin.

/* ppecran.cpp programme principal ecran */

#include <stdio.h>

#include "ecran.h"

void main () {

initialiserEcran (20, 50);

rectangle ( 3, 10, 43, 17); // maison rectangle ( 3, 4, 43, 10); // toiture rectangle (20, 12, 23, 17); // porte rectangle (41, 1, 43, 4); // cheminée rectangle (10, 12, 14, 15); // fenêtre gauche rectangle (30, 12, 34, 15); // fenêtre droite ecrireMessage (19, 15, "Maison de rêves");

afficherEcran ();

sauverEcran ("maison.res");

detruireEcran ();

}

1.4.2.d Le résultat de l’exécution du test du module écran

Après exécution du programme d’application ppecran.cpp précédent, le fichier maison.res contient le dessin suivant.

***

* * * * *****************************************

* * * * * * * * * * *****************************************

* * * ***** **** ***** * * * * * * * * * * * * * * * * * * ***** * * ***** * * * * * *****************************************

Maison de rêves

Figure 17 Dessin d’une maison avec le module ecran.

La notion de classe en programmation objet correspond à l’extension de la notion de module. Une classe comprend des objets (données propres) et des fonctions appe-lées méthodes qui gèrent ses objets. Sur l’exemple, il n’y a qu’un seul écran ; si on

02Chap_01 Page 29 Samedi, 17. janvier 2004 10:36 10

30 1 Récursivité, pointeurs, modules

veut pouvoir gérer plusieurs écrans, il faut passer en paramètre de chaque fonction un pointeur d’écran : un pointeur sur une structure contenant les données spécifiques de chaque écran. En programmation (orientée) objet, on définirait une classe ecran, chaque élément de cette classe ayant ses propres données. D’autres mécanismes plus spécifiques de la programmation objet ne sont pas abordés ici comme l’héritage ou les méthodes virtuelles.

L’exemple donné pourrait être complété en définissant d’autres fonctions mises à la disposition de l’utilisateur. On pourrait définir des fonctions de tracé de figures géométriques (cercles, carrés, ellipses, etc.). Les directions de déplacement devraient être quelconques ; on devrait pouvoir avancer de n pas suivant un angle donné. On devrait pouvoir tourner à droite ou à gauche d’un angle donné. On pour-rait refaire de cette façon le langage Logo qui facilite le dessin sur écran et l’appren-tissage de la programmation.

Exercice 4 - Spirale rectangulaire (récursive)

En utilisant le module ecran défini précédemment, écrire la fonction récursive void spirale (int n, int lgMax) ; qui trace la spirale de la Figure 18 ; n est la longueur du segment ; lgMax est la longueur du plus grand segment à tracer.

...

. . . ... . . . . . . . ... . . . . . . . . . . . . . . . . . ... . . . . . . . . . ... . . . . . ...

. .

Figure 18 Dessin d’une spirale rectangulaire.

Exercice 5 - Module de gestion de piles d’entiers (allocation contiguë)

Une pile d’entiers est une structure de données contenant des entiers qui sont gérés en ajoutant et en retirant des valeurs en sommet de pile uniquement. On utilise une allocation contiguë : un tableau d’entiers géré comme une pile ; c’est-à-dire où les ajouts et les retraits sont effectués au sommet de la pile uniquement. Le type Pile

02Chap_01 Page 30 Samedi, 17. janvier 2004 10:36 10

1.4 • Modules 31

© Dunod – La photocopie non autorisée est un délit.

décrit une structure contenant une variable max indiquant le nombre maximum d’éléments dans la pile, une variable nb repérant le dernier occupé de la pile (sommet de pile) et le tableau element des éléments de la pile. Les fonctions à appli-quer à la pile consistent à initialiser une pile en allouant dynamiquement le tableau des max entiers, à tester si la pile est vide ou non, à ajouter une valeur en sommet de pile s’il reste de la place, à extraire une valeur du sommet de pile si la pile n’est pas vide, à lister pour vérification toutes les valeurs de la pile, et à détruire la pile. Le fichier d’en-tête pile.h est le suivant :

/* pile.h version avec allocation dynamique du tableau */

#ifndef PILE_H

#define PILE_H typedef struct {

int max; // nombre maximum d'éléments dans la pile int nb; // repère le dernier occupé de element int* element; // le tableau des éléments de la pile } Pile;

Pile* creerPile (int max);

int pileVide (Pile* p);

void empiler (Pile* p, int valeur);

int depiler (Pile* p, int* valeur);

void listerPile (Pile* p);

void detruirePile (Pile* p);

#endif

La fonction int depiler (Pile* p, int* valeur) ;

• fournit 0 (faux) si la pile est vide, 1 (vrai) sinon.

• met dans l’entier pointé par valeur, et la supprime de la pile, la valeur en sommet de pile (cas de la pile non vide).

Écrire le corps du module pile.cpp et un programme de test pppile.cpp (programme principal des piles) contenant un menu permettant de vérifier tous les cas. Le menu est proposé ci-dessous.

GESTION D'UNE PILE 0 - Fin

1 - Création de la pile 2 - La pile est-elle vide ? 3 - Insertion dans la pile 4 - Retrait de la pile 5 - Listage de la pile Votre choix ?

02Chap_01 Page 31 Samedi, 17. janvier 2004 10:36 10

32 1 Récursivité, pointeurs, modules

Exercice 6 - Module de gestion de nombres complexes

Un nombre complexe se compose d’une partie réelle et d’une partie imaginaire.

Les fonctions que l’on peut réaliser sur les complexes sont données ci-dessous :

• fonction (crC) permettant la création d’un nombre complexe à partir de 2 réels,

• fonction (crCP) permettant la création d’un nombre complexe à partir de son module, et de son argument en radians entre -π et +π,

• fonctions (partReelC, partImagC, moduleC, argumentC) délivrant la partie réelle, la partie imaginaire, le module ou l’argument d’un nombre complexe,

• fonctions (ecritureC, ecritureCP) faisant l’écriture des 2 composantes d’un nombre complexe sous la forme (x + y i) comme par exemple (2 + 1.5 i), ou sous la forme en polaire (2.5, 0.64),

• fonctions (opposeC, conjugueC, inverseC, puissanceC) délivrant le complexe opposé, le conjugué, l’inverse ou une puissance entière d’un nombre complexe,

• fonctions (additionC, soustractionC, multiplicationC, divisionC) faisant l’addition, la soustraction, la multiplication ou la division de deux nombres complexes.

Le fichier d’en-tête complex.h décrivant l’interface du module est le suivant. Le type Complex est décrit comme une structure de deux réels partReel et partImag.

/* complex.h */

#ifndef COMPLEX_H

#define COMPLEX_H

#define M_PI 3.1415926535 typedef struct {

double partReel; // partie réelle double partImag; // partie imaginaire } Complex;

// les constructeurs

Complex crC (double partReel, double partImag);

Complex crCP (double module, double argument);

double partReelC (Complex z);

double partImagC (Complex z);

double moduleC (Complex z);

double argumentC (Complex z);

void ecritureC (Complex z);

void ecritureCP (Complex z);

Complex opposeC (Complex z);

Complex conjugueC (Complex z);

Complex inverseC (Complex z);

Complex puissanceC (Complex z, int n);

Complex additionC (Complex z1, Complex z2);

Complex soustractionC (Complex z1, Complex z2);

02Chap_01 Page 32 Samedi, 17. janvier 2004 10:36 10