TD 3

1 - Héritage et Polymorphisme

Le concept de Programmation Orientée Objet permet de factoriser du code commun entre plusieurs classes dans une classe mère : c'est l'héritage. Si ce concept ne vous parle pas, il est expliqué en détail ici : http://www.cplusplus.com/doc/tutorial/inheritance/#inheritance

Pour améliorer votre application, vous allez ajouter de nouvelles formes : carré, losange et courbes quelconques. Toutes ces formes possèdent certaines données génériques en commun, mais sont toutes paramétrables différemment : taille pour le carré, hauteur/largeur pour le rectangle, rayon pour le cercle, etc.

1. En utilisant les mécanismes du C++, définissez une classe générique Shape qui stockera les attributs et proposera les méthodes associées au positionnement d'une forme. Les attributs doivent avoir la bonne visibilité pour être accessible dans la classe mère et les classes filles directement, mais pas depuis l'extérieur (il faudra pour cela utiliser des accesseurs).

Attention, lorsque vous faites de l'héritage, n'oubliez pas de toujours spécifier vos destructeurs en virtual : lorsque l'objet d'une classe fille est détruit, le compilateur doit pouvoir appeler le destructeur de cette classe (et non pas celui du type déclaré, qui peut être différent). Pour plus d'informations sur les cas critiques générés par l'héritage sur les constructeurs et destructeurs, vous pouvez jeter un coup d’œil à : http://www.cprogramming.com/tutorial/constructor_destructor_ordering.html

2. Ajoutez aussi le stockage d'un facteur d'échelle dans cette classe (toujours avec les méthodes associées), que vous utiliserez pour animer le grossissement des formes indépendamment de leur géométrie

3. Pour la suite des opérations, il est nécessaire de proposer une méthode virtuelle pure qui retournera le nom de la classe de l'objet (Shape, Square, Circle, etc.) :

virtual std::string Shape::getName() = 0;

Plus d'info sur les fonctions virtuelles : http://www.cplusplus.com/doc/tutorial/polymorphism/#virtual et virtuelles pures http://www.cplusplus.com/doc/tutorial/polymorphism/#abstract

4. Cette classe doit aussi définir une méthode virtuelle pure draw, sans en fournir d'implémentation, celle-ci devant être fournie dans les classes filles.

5. Modifiez la classe Square pour qu'elle hérite de Shape et respecte l'interface qu'elle définie. Ajoutez une autre classe d'objet graphique (Diamond par exemple).

2 - Templates : classe std::vector

Les templates permettent de spécialiser du code lors de la compilation, et ainsi optimiser les performances. En outre, ils permettent d'augmenter considérablement la généricité du code produit, facilitant la maintenance. En conclusion, si les templates peuvent paraître rebutantes à première vue, l'apprentissage des mécanismes les utilisant est un réel atout pour un développeur C++.

1. Pour vous familiariser avec l'utilisation des classes templates, ajoutez à la classe Viewer un tableau dynamiques std::vector de pointeurs sur des objets Shape.

2. Utilisez un itérateur sur ce tableau dynamique pour le parcourir lors de l'affichage et de la mise à jour des positions. Que constatez-vous ?

3. Modifiez votre application pour que l'utilisateur puisse ajouter un nouvelle forme à l'exécution en appuyant sur des touches du clavier (une touche par type de forme). La position de la forme peut être générée aléatoirement.

3 - Templates : utilisation

Pour cet exercice, vous allez exploiter tous les composants que vous avez développé lors des séances précédentes, à savoir les flux d'entrée/sorties, les opérateurs mathématiques des classes vecteur, l'héritage et les tableaux dynamiques de la STL.

1. Ajoutez une classe Curve, héritant de Shape. Cette classe doit être créée en prenant le nom d'un fichier en paramètre (fichier à télécharger sinus1.curv), afin de charger une suite de coordonnées 2D définissant la forme.

Remarque : vous pouvez utilisez les classes et méthodes std::ifstream, std::getline et std::istringstream pour "parser" le fichier.

Comme vous ne connaissez pas le nombre de points composant le courbe, utilisez une tableau dynamique pour stocker les positions.

Comme vous ne connaissez pas la taille de la forme, vous devez calculer sa boite englobante (valeur minimum et maximum pour x et y), puis centrer la forme en (0,0) (en soustrayant aux coordonnées des points le centre de la boite englobante) et normaliser toutes les coordonnées entre -0.5 et 0.5. Toutes ces opérations doivent être réalisées en utilisant les opérateurs mathématiques des vecteurs (addition de deux vecteur, multiplication par un scalaire, etc.).

2. Contrairement aux forme prédéfinies, affichez uniquement le contour de la forme dans la méthode draw. Pour tracer une ligne entre les vecteurs p1 et p2 utilisez :

glBegin (GL_LINES);
glVertex2f (p1.x(), p1.y());
glVertex2f (p2.x(), p2.y());
glEnd ();

3. Utilisez cette forme dans votre application (création lors d'un événement clavier, animation du grossissement, etc.).

4 - Templates : première classe

Cet exercice se focalise sur la classe vec. En effet, cette classe peut être utilisable dans de nombreuses applications si elle est rendue suffisamment générique. On peut par exemple l'utiliser pour afficher des objets 3D au lieux des formes 2D.

1. Supprimez la classe vec2f et créez une nouvelle classe vec prenant en paramètre de template la taille du vecteur. Utilisez un typedef pour redéfinir le type vec2f.

2. Ajoutez un paramètre template Scalar indiquant le type stocké dans le vecteur (float, double, etc). Modifiez votre typedef en conséquence.

3. Créez un nouveau type vec3d pour stocker la couleur des forme. Ajoutez à la classe Shape cet attribut de couleur et utilisez-le lors du dessin.