Vous êtes sur la page 1sur 5

INF1005CN - PROGRAMMATION PROCÉDURALE

Travail dirigé No. 4


Les fonctions.

Objectifs : Permettre à l’étudiant de saisir le concept des fonctions et de la programmation


procédurale.
Durée : Deux séances de laboratoire.
Remise du travail : Jour de la 2ème séance de TP, avant 23 h 30.
Travail préparatoire : Lecture des exercices et rédaction des algorithmes.
Documents à remettre : Sur le site Moodle des travaux pratiques, vous remettrez le fichier .cpp en suivant
la procédure de remise des TD.
Retard : Les retards ne sont pas tolérés, un retard méritera la note 0.

Directives particulières
• Vous pouvez déclarer toutes les variables locales désirées.
• Aucune variable globale (les constantes globales sont permises).
• Vous pouvez déclarer des fonctions supplémentaires pour améliorer la lisibilité du programme ou
adhérer au principe de « don’t repeat yourself ».
• N’oubliez pas de mettre les entêtes de fichiers, de respecter le guide de codage et de placer des
commentaires dans le code aux endroits appropriés.
• Des pénalités sont associées au non-respect des points du guide de codage résumés en dernière page
de ce document.
• Utilisez le type string pour les chaînes de caractères.
• Il est interdit de faire un affichage, directement ou indirectement, dans les fonctions décrites dans
l’énoncé, à moins que ce soit spécifié autrement dans la description de la fonction.
• Il est interdit d’ajouter une attente à la fin de l’exécution du programme; pour exécuter votre
programme, utiliser l’option « exécuter sans débogage » dans l’environnement de développement (ou
ctrl-F5), pour que l’environnement fasse lui-même l’attente à la fin.

Images fractales par IFS


Nous voulons un programme qui produit des images fractales par systèmes de fonctions itérées
(souvent appelé par l’acronyme anglais IFS). Le programme pourra, entre autres, produire les cinq
images suivantes :

Idée générale de la méthode


Le « système de fonctions » est un ensemble de transformations applicables sur des points du plan
cartésien. Chaque transformation pouvant faire une mise à l’échelle, une rotation, une translation, et
autres transformations linéaires. Le mot « itérées » signifie que ces transformations seront appliquées

1/5
itérativement, c’est-à-dire que le résultat d’une transformation sera passé dans une autre
transformation, puis une autre.
Pour générer ces images, nous allons utiliser une méthode stochastique (aléatoire). Un assez grand
nombre de points seront générés aléatoirement dans le carré unité ( x et y étant chacun dans [0,1[ ).
Pour chacun de ces points, une série de transformations seront effectuées, prises aléatoirement parmi
l’ensemble des transformations définies. Chaque point résultant sera tracé dans une image (en
excluant quelques points au départ, qui n’ont pas été assez influencés par les transformations).
Les images sont donc le résultat de l’application aléatoire de transformations sur des points générés
aléatoirement.
Représentation de l’image
L’image est un tableau de 500 par 500 pixels (« pixel » vient de la contraction de « picture
element »). Chaque pixel est simplement une valeur de 0 à 255 qui représente son intensité; 0 pour
noir, 255 pour blanc, et les valeurs entre les deux pour les teintes de gris. L’axe x sera placé
horizontalement, et correspond au numéro de colonne du tableau, et l’axe y sera placé verticalement,
et correspond au numéro de ligne du tableau.
Nous avons défini un type « Pixel » (équivalent à « unsigned char ») pour représenter les valeurs des
pixels dans le tableau. Ce type ne peut prendre que des valeurs de 0 à 255.
Représentation d’une transformation linéaire
L’IFS est composé d’un ensemble de transformations. Nous allons représenter une transformation
par un tableau de 3 lignes par 2 colonnes :
a b
c d
e f
Dans ce tableau, les deux premières lignes représentent une matrice 2x2, et la dernière ligne
représente un vecteur colonne 2x1. Un point (x, y) est transformé en (x’, y’) par l’équation :
 x′   a b   x   e 
  =     +  
 y′   c d   y   f 
Sous forme textuelle dans un fichier, les 6 valeurs réelles seront écrites dans l’ordre « a b c d e f »,
avec un ou plusieurs espaces entre.
Fonctions à écrire
Ce TD demande d’écrire un seul programme constitué de plusieurs fonctions.
• borneDansIntervalle prend en paramètres une valeur à borner, la valeur de la borne
inférieure et la valeur de la borne supérieure. Elle retourne la valeur de l’intervalle [borne
inférieure, borne supérieure] qui est la plus proche de la valeur à borner, c’est-à-dire que si
la valeur est déjà dans l’intervalle, elle est retournée inchangée, et si la valeur est à l’extérieur
de l’intervalle, la borne du côté où la valeur dépasse est retournée.

• estDansIntervalle prend les mêmes paramètres que borneDansIntervalle. Elle retourne vrai
si la valeur à borner est dans l’intervalle, faux sinon.

• dessinePoint prend en paramètres les coordonnées x et y du point, une intensité entière, ainsi
que l’image à modifier. La valeur pour le pixel de l’image de coordonnées x, y est réduite
(soustraction) de intensité; le pixel devient donc plus foncé. Il faut s’assurer que la valeur
résultante est bornée entre 0 et 255 (valeurs possibles pour les pixels) et il faut modifier la
valeur du pixel uniquement si les coordonnées sont dans l’image (coordonnées de 0 à 499).

2/5
• aleatoireZeroUn ne prend aucun paramètre. Elle retourne une valeur réelle entre 0.0 et 1.0.

• aleatoireSelonDistribution prend en paramètre un tableau de probabilités cumulatives d’une


distribution discrète. La dernière probabilité cumulative est toujours 1.0 car les probabilités
d’une distribution somment à 1.0. Elle retourne un entier aléatoire suivant la distribution.
Algorithme : générer une valeur aléatoire x entre 0.0 et 1.0, puis trouver le plus petit indice i
pour lequel la probabilité cumulative à l’indice i est supérieure à x. Par exemple, pour les
probabilités cumulatives { 0.1, 0.35, 1.0 }, on veut retourner 0 si x (la valeur générée
aléatoirement entre 0.0 et 1.0) est plus petit que 0.1, retourner 1 si x est entre 0.1 et 0.35, et
retourner 2 si x est entre 0.35 et 1.0 (x ne peut pas être supérieur à 1.0, donc ça couvre bien
tous les cas).

• transformePoint prend en paramètres une transformation (un tableau, voir section ci-
dessus), une coordonnée x, et une coordonnée y. Elle modifie les valeurs de x et y pour
qu’elles deviennent les valeurs x’ et y’ transformées (voir l’équation ci-dessus). Elle ne
retourne rien.

• tests ne prend aucun paramètre, et ne retourne rien. Elle affiche les résultats d’appels à
certaines fonctions ci-dessus, pour vérifier qu’elles sont correctes. Voir plus bas pour les
détails.

• calculerImage prend en paramètres le nom du fichier d’entrée (fichier texte contenant les
transformations) et le nom du fichier de sortie (fichier .bmp). Elle crée l’image selon le
fichier d’entrée. Voir plus bas pour les détails.

• main doit appeler la fonction tests, puis calculerImage pour les 5 fichiers donnés en exemple.

Pour chaque fonction, vous devez clairement identifier quels paramètres sont OUT et INOUT, et
utiliser de manière correcte « const » lorsqu’un paramètre est IN mais qu’il est passé par adresse.
Sauf pour les fonctions « tests » et « calculerImage », chaque fonction décrite ci-dessus s’écrit en
moins de 8 lignes, excluant les lignes vides ou contenant uniquement des accolades. (Ceci n’est pas
une contrainte, uniquement une indication.)
Tests
Cette fonction devrait être écrite au fur et à mesure que vous écrivez les fonctions ci-dessus, pour
vérifier qu’elles fonctionnent correctement. Pour tester vos fonctions :
- Appeler borneDansIntervalle pour chaque valeur entière de -1 à 4 (inclus), avec les bornes [1,
3], et afficher les résultats; devrait donner : 1 1 1 2 3 3.
- Appeler estDansIntervalle pour les mêmes valeurs et bornes que ci-dessus, et afficher les
résultats; devrait donner : 0 0 1 1 1 0 (ou la même chose en true/false si boolalpha est
activé).
- Déclarer un tableau image, le remplir de valeurs à 255 (blanc), puis appeler dessinePoint
pour ajouter des points à l’image, avec les coordonnées et intensités suivantes (x, y,
intensité) : (10, 10, 255), (20, 15, 100), (30, 20, 128), (-1, 1, 255), (500, 1, 255), (1, -1, 255),
(1, 500, 255), (30, 20, 128). Appeler « ecrireImage(image, "imageTest.bmp"); » pour écrire
l’image résultante sur le disque. Cette image devrait contenir trois points en ligne diagonale
dans le coin inférieur gauche; celui au centre des trois devrait être plus pâle que les deux
autres qui devraient être parfaitement noirs.

3/5
- Appeler aleatoireSelonDistribution mille fois avec la distribution { 0.1, 0.35, 1.0 }, et
compter le nombre de fois où la valeur retournée est 1, puis afficher ce compte. Devrait être
très proche de 250. (Noter que ceci teste en même temps aleatoireZeroUn.)
- Appeler transformePoint avec la transformation { { 0.5, -0.5 }, { 0.25, 0.75 }, { 2.0, 3.0 } } à
appliquer sur le point x = 0.2, y = 0.7, puis afficher le point résultant. Le résultat devrait être
1.75, 3.575.
CalculerImage
Cette fonction doit commencer par lire le fichier texte donnant les informations sur comment générer
l’image. Le format du fichier d’entrée est le suivant, les valeurs étant séparées par un ou plusieurs
espaces :
• Sur la première ligne :
o le nombre de points de départ générés aléatoirement;
o le nombre de transformations à appliquer à chaque point;
o le nombre de transformations à appliquer avant de commencer à afficher les
points;
o l’intensité des points affichés (valeur passée à la fonction dessinePoint).
• Sur la deuxième ligne : la transformation d’affichage (6 valeurs).
• Sur les lignes suivantes jusqu’à la fin du fichier, une ligne par transformation de l’IFS,
chaque ligne contient :
o la transformation (6 valeurs)
o la probabilité cumulative en pourcentage (une valeur entre 0 et 100) où cette
transformation sera appliquée (distribution utilisée pour déterminer quelle
transformation appliquer, en utilisant aleatoireSelonDistribution).
(Les 6 valeurs pour la transformation sont dans l’ordre décrit dans la section « Représentation d’une
transformation linéaire » de ce document.)
Elle doit ensuite déclarer une image et l’initialiser uniformément blanc (tous les pixels à la valeur
255).
Puis, l’algorithme pour calculer l’image est le suivant :
Répéter « nombre de points de départ » fois :
Générer des coordonnées x, y entre 0 et 1 aléatoirement.
Répéter « nombre de transformations à appliquer » fois :
Déterminer l’indice de la transformation à appliquer, suivant la distribution aléatoire.
Appliquer la transformation sur les coordonnées x, y.
Si on a appliqué sur ce point au moins « le nombre de transformation à appliquer avant de
commencer à afficher les points » :
Appliquer la « transformation d’affichage » sur une copie des coordonnées x, y (cette
transformation doit donner des coordonnées utilisées uniquement pour
l’affichage, et non modifier les valeurs de x, y sur lesquelles sont appliquées les
transformations de l’IFS).
Appeler dessinePoint sur les coordonnées résultantes, avec l’« intensité » dite sur la
première ligne du fichier d’entrée, pour modifier l’image.

Puis finalement, il faut écrire l’image résultante sur le disque (avec ecrireImage).
Fonction fournie
La fonction ecrireImage est donnée dans le fichier « ecrireimage.h ». Vous n’avez qu’à placer ce
fichier au même endroit que votre projet et fichier .cpp, puis faire « #include "ecrireimage.h" » avec
les autres #include (noter qu’il faut bien utiliser les guillemets " " et non < > autour de ce nom de

4/5
fichier). Cette fonction prend comme premier paramètre une image (tableau de 500 par 500 valeurs
de type Pixel) et comme deuxième paramètre le nom du fichier dans lequel l’image sera écrite. Elle
retourne vrai si l’écriture s’est faite correctement, faux sinon.

Tous les points du guide nommés au TD3 devraient normalement encore être respectés, mais voici un
résumé des points sur lesquels nous insistons pour ce TD :

3 et 5 : nom des variables et des fonctions en lowerCamelCase


33 : ajouter un entête de fichier
47 : pas plus d’une signification par variable
48 : aucune variable globale
50 : mettre le & près du type
52 : variables vivantes le moins longtemps possible
62 : pas de nombres magiques dans le code
85 : mieux écrire plutôt que commenter
89 : entêtes de fonctions; y indiquer clairement les paramètres [out] et [in,out]

Ainsi qu’un point qui n’est pas actuellement dans le guide : « don’t repeat yourself ». Ceci
signifie qu’il faut tenter d’éviter d’écrire la même chose plus d’une fois dans un programme. Les
instructions de répétition ainsi que la définition de fonctions sont des outils que vous avez vus et
qui permettent de réduire les répétitions d’écriture dans le programme.

5/5

Vous aimerez peut-être aussi