TD 1

1 - Les classes en C++

À partir de maintenant et jusqu'à la fin du cours, vous êtes supposé avoir à disposition le tutoriel http://www.cplusplus.com/doc/tutorial/. Ce document vous servira à trouver les informations pertinentes pour la résolution des exercices.

Dans ce second exercice, vous allez développer les premières briques de votre application.

1. Lisez, analysez et compilez le projet viewer. Ce projet utilise l'utilitaire cmake pour sa compilation (il vous faut ajouter le chemin /opt/local/bin à votre PATH pour y accéder). Il s'utilise ainsi (depuis le dossier viewer) :

$ export PATH=/opt/local/bin:$PATH
$ mkdir build
$ cd build
$ cmake ..
$ make
Comme vous pouvez le constater lors de l'exécution du projet, un carré rouge est affiché au centre de l'application. Observez la classe Viewer, elle contient un ensemble de méthodes pour l'initialisation du contexte OpenGL, l'affichage de la scène et la gestion des événements clavier/souris.

Vous pouvez vous inspirer de cette classe pour la syntaxe à utiliser dans les questions suivantes, ainsi que de la documentation présentée ici : http://www.cplusplus.com/doc/tutorial/classes/. N'oubliez pas que le fichier d’entête (viewer.h) contient les déclarations des méthodes et des variables. Tout ce qui y est défini est accessible dans le reste de votre application.

2. Comme vous pouvez le voir, l'affichage du carré est effectué dans la méthode drawRedSquare. Créez la structure vec2f et la classe Square telles que :

  • vec2f soit un structure codant des vecteurs a deux dimensions :
    struct vec2f {
       float x;
       float y;
    };
  • la classe Square ait pour attributs les positions et dimensions du rectangle :
    vec2f _dim;
    vec2f _pos;

3. Modifiez la classe Viewer pour y inclure un objet Square :

private:
    Square _square;

Initialisez les valeurs (dimension et position) de l'instance dans la méthode Viewer::init. Modifiez les méthodes Viewer::drawRedSquare et Viewer::updateScene pour utiliser les informations stockées dans l'instance lors de l'affichage.

2 - Constructeurs / Destructeurs

Dans les exercices précédents, nous avons utilisé une structure simple pour stocker les vecteurs 2D. Cette structure est utile, mais propose très peu de fonctionnalités (juste le stockage). À partir de maintenant, nous allons travailler sur la structure vec2f pour la rendre plus utile.

1. Pour éviter toute confusion, changez votre structure en classe et définissez celle-ci dans un nouveau fichier d'entête vec2.h. Compilez. Pouvez vous toujours accéder aux variables dans le reste de votre code ? Qu'en déduisez vous de la nature d'une structure en C++ ?

Les constructeurs sont des méthodes un peu particulières qui permettent d'initialiser la mémoire interne des instances d'une classe : allocation des zones mémoire allouées dynamiquement, valeurs par défaut des variables, etc. Leur syntaxe permet de les différencier des autres méthodes : ils n'ont pas de valeur de retour mais possède le même nom que la classe.

// myClass.h
MyClass();       
// myClass.cpp
MyClass::MyClass(){ }

Comme toutes les fonctions en C++, il est possible de surcharger le constructeur, et ainsi de définir un comportement différent en fonction des paramètres. Par exemple :

MyClass();
MyClass(int foo);

Dans tous les cas, le constructeur ne prenant pas de paramètre est appelé constructeur par défaut. C'est lui qui est appelé à chaque fois que l'on crée l'objet sans passer de paramètre (lors de la création d'un tableau d'objets par exemple). Si vous ne définissez pas de constructeur par défaut, les variables membres de la classe ne seront pas initialisées.

2. Ajoutez un constructeur par défaut à votre classe vec2f qui initialisera ses coordonnées à (0,0).

3. Surchargez le constructeur en ajoutant la possibilité d'initialiser le vecteur par une valeur constante pour toutes les coordonnées.

4. Combinez les deux constructeurs précédents en un seul en utilisant le mécanisme des arguments par défaut (http://en.cppreference.com/w/cpp/language/default_arguments). Modifiez le code de la classe Viewer pour utiliser ces constructeurs.

5. Comme leur nom l'indique, les constructeurs par copie permettent de copier une instance dans une autre :

MyClass A;
MyClass B(A);

Ajoutez un constructeur par copie dans votre classes vec2f :

vec2f(const vec2f& other);

Lisez cet article sur les constructeurs par copie http://www.cplusplus.com/articles/y8hv0pDG/

3 - Fonctions et méthodes

Une méthode est définie comme une fonction membre. L'idée est qu'au même titre qu'une instance peut posséder des attributs membres, elle peut aussi exécuter une méthode, par exemple :

// code C
foo( myObject );

// code C++ 
MyClass myObject;
myObject.foo();

MyClass *myObject2 = &myObject; // pointeur sur l'adresse mémoire de l'objet
myObject2->foo();

1. Ajoutez une méthode draw() à la classe Square. Elle reprendra le code défini dans Viewer::drawSquare en utilisant les coordonnées (position et dimension) stockées dans l'instance. Appelez cette méthode la place de Viewer::drawRedSquare dans la fonction Viewer::display.

2. Ajoutez une méthode setPosition(vec2f v) à votre classe Square, qui change la position de l'instance concernée. Utilisez-la dans la fonction Viewer::updateScene.

3. Ajoutez une méthode setPosition(float x, float y). Comme elle porte le même nom mais prend des paramètres différents de setPosition(vec2f v), on dit que l'on a surchargé la méthode.

4. Ajoutez à la classe Square un attribut vec2f _speed stockant le vecteur vitesse de l'instance, et une méthode setSpeed(vec2f speed). Cette méthode doit être appelée dans la fonction mousePressed, lorsque l'utilisateur clique avec la souris. Le carré devra alors se déplacer dans la direction du clic souris.

Ajoutez un coefficient de ralentissement pour éviter que le carré ne sorte de l'écran.

Appelez setSpeed() lors de updateScene pour déplacer la forme dans une direction aléatoire.