Buts: Permettre à la simulation de basculer vers une vue interne correspondant à un fragment d'organe d'un hamster sélectionné.
Le but est maintenant de doter nos animaux d'organes dont il sera possible de visualiser un fragment en basculant sur une vue interne de la simulation. Nous nous contenterons d'un organe simulé générique (supposé être le même pour tous les animaux) et allons commencer par mettre en place le mécanisme qui permettra de sélectionner l'individu observé.
Le noyau de simulation (classe Application) vous est fourni dans cette étape dans une nouvelle version qui permet le basculement de la simulation sur différentes «vues». Il y est notamment fait usage du type énuméré View défini dans Types.hpp. La valeur LAB de ce type désigne la vue externe (laboratoire avec hamsters), la valeur ECM désigne la vue interne et la valeur CONCENTRATION désigne la vue interne en mode observation de la diffusion des substances. Jetez un oeil à la nouvelle version de la classe Application, notamment aux contrôles qu'elle offre pour mieux en comprendre l'utilisation par la suite.
Il vous est demandé dans un premier temps de coder la méthode void Lab::trackAnimal(const Vec2d&) permettant d'indiquer que l'on se focalise sur un animal particulier dans le laboratoire (cette méthode est utilisée pour la gestion de la touche 'T' dans FinalApplication).
L'appel à la méthode void Lab::trackAnimal(const Vec2d&) devra permettre de répertorier l'animal à la position donnée comme «traqué». Pour qu'un animal soit considéré comme étant à la position donnée p, il faudra que la distance entre sa position et p soit inférieure à son rayon. Un animal «traqué» sera dessiné assorti d'une icône particulière (une texture est prévue via getAppConfig().entity_texture_tracked) :
Le hamster traqué est dessinée avec la petite icône «cible» |
[Question Q4.1] Cela ne fait pas sens de traquer des tas de granulés. Pour éviter d'avoir à tester le type des entités pour mettre en oeuvre la méthode trackAnimal(const Vec2d&), il est possible par exemple de :1) introduire une collection d'animaux en parallèle de la collection d'entités ou 2) d'introduire une méthode polymorphique indiquant si une entité peut être traquée ou pas. Quelle méthode vous semble préférable et pourquoi :?
Le code est compilable à ce stade, vous pouvez décommenter la cible application. Il devrait être possible de marquer un hamster comme traqué avec la touche T (la petite icône rose s'affiche alors dessus) et cesser de traquer tout hamster avec la touche Z. Veillez à décommenter au préalable la gestion des touches T et Z dans Tests/GraphicalTests/FinalApplication.cpp.
La classe Organ que nous vous proposons d'introduire maintenant va représenter un fragment d'organe reposant sur une matrice extra-cellulaire («ECM») et irriguées par des cellules sanguines:
Le modèle que nous utiliserons consiste à quadriller le fragment au moyen d'une grille dont chaque case représentera un empilement de cellules. Dans l'image ci-dessus, délibérément donnée avec une taille assez grossière des cases, chaque carré rouge représente une cellule du réseau sanguin reposant sur une cellule de l'organe qui elle même repose sur une cellule de l'ECM. Chaque carré rose foncé représente une cellule de l'organe reposant sur une cellule de l'ECM. Le fond rose clair désigne les cases occupées uniquement par une cellule de l'ECM.
Pour modéliser un Organ comme une grille, commencez par introduire des attributs représentant le nombre de cases par ligne (un int) ainsi que la taille graphique de chaque cellule (un float). Nous désignerons ces attributs sous les noms de nbCells_ et cellSize_ dans ce qui suit mais libre à vous de nommer ces attributs comme vous le souhaitez.
Un Organ est un élément de la simulation dont nous allons raisonnablement pouvoir supposer:
Pour le moment, les méthodes Organ::update et Organ::drawOn ont un corps vide et le but des étapes à venir du projet et de les remplir. Il s'agira de simuler un organe dont la représentation au départ ressemblera à l'image ci-dessus.
La méthode drawOn aura donc pour vocation de dessiner des carrés texturés, en fonction de la nature des cellules empilées au niveau de chaque case.
Pour dessiner de nombreux carrés texturés, il est plus efficace avec SFML de faire le rendu dans une texture (d'un type nommé sf::RenderTexture ) et d'afficher cette texture en une seule fois dans Organ::drawOn (plutôt que d'afficher directement chaque carré directement dans la fenêtre graphique).
Déclarez donc dans la classe Organ un attribut, appelons-le organTexture_, de type sf::RenderTexture représentant l'image associée à l'organe pour son dessin. La méthode de dessin d'un organe aura ainsi un corps tout simple ressemblant à ceci:
sf::Sprite image(organTexture_.getTexture()); // transforme l'image en texture target.draw(image); // affiche la texture
Les modalités d'initialisation et de mise à jour de l'attribut organTexture_ sont ébauchées dans ce qui suit.
La génération du contenu d'un organe (qui déterminera quels types de cellules s'empilent au niveau de chaque case) est une procédure relativement complexe et il fait sens d'en faire une méthode à part. Introduisez à cet effet une méthode Organ::generate() en accès protégé.
Nous vous suggérons l'algorithme en cinq étapes ci-dessous pour sa mise ne oeuvre. Pour une meilleure modularisation, chaque étape fera l'objet d'une méthode spécifique. L'ensemble des méthodes peuvent être aussi en accès protégé, hormis updateRepresentation qui pourra être publiquement sollicitée. Vous conserverez les noms suggérés si vous voulez utiliser les tests fournis sans retouches.
Vous pouvez donc anticiper la présence de chacune de ces méthodes. createOrgan et createBloodSystem auront un corps vide pour le moment (ce sera l'objet de modules à venir). Nous ébauchons les autres méthodes dans ce qui suit.
[Question Q4.2] Nous vous suggérons de coder la méthode generate comme virtuelle. En quoi cela peut-être utile selon vous ? Répondez à cette question dans votre fichier REPONSES.
Cette méthode doit :
Pour être compatible avec les tests fournis, la largeur et la hauteur graphiques associées à la vue interne seront respectivement retournées par des méthodes publiques Organ::getWidth et Organ::getHeight. Ces deux «getters» retourneront tous les deux la valeur paramétrable getAppConfig().simulation_organ_size.
Cette méthode est charge d'initialiser l'attribut organTexture_ décrit plus haut. Pour le moment nous nous contenterons d'initialiser la taille de l'image que représente organTexture_. Ceci se fera au moyen d'une tournure de cette nature :
organTexture_.create(horizontalSize, verticalSize);
Dans notre cas, horizontalSize et verticalSize auront la même valeur (nombre de cellules par ligne multiplié par la taille graphique d'une cellule).
A ce stade, cette méthode efface simplement l'image organTexture_ en lui donnant un fond tout rose, ce qui peut se faire au moyen du code suivant :
organTexture_.clear(sf::Color(223,196,176)); organTexture_.display();
Sur ce fond rose, seront dessinés par la suite carrés de couleurs/textures différentes.
Faites maintenant en sorte qu'un Animal soit doté d'un Organ* (nous utilisons les pointeurs car il n'est pas exclu que nous ayons recours à de la «transplantation» d'organes, comme vous pourrez le voir au niveau ce certains programmes de test ;-)).
Vous doterez Organ d'un constructeur prenant en paramètre un booléen generation prenant true comme valeur par défaut. Le constructeur fera appel à generate si generation vaut true et ne fera rien sinon. Ceci permettra de générer, à la demande, des organes vides de contenu ou doté d'un contenu. Faites ensuite en sorte qu'un animal soit doté d'un organe dynamiquement alloué (avec un contenu) à sa construction.
Complétez enfin dans votre classe Lab:
[Question Q4.3] Quelles autres modifications devez vous apporter à votre programme pour coder ces méthodes? En particulier, comment proposez vous de coder updateTrackedAnimal() et drawCurrentOrgan sans coder de «getter» sur l'organe de l'animal (trop intrusif) ? Répondez à ces questions dans votre fichier REPONSES et apportez les modifications suggérées.
Nous avons maintenant mis en place les briques de base permettant le dessin et la génération d'un organe. Il est temps de tester le basculement vers la vue interne.
Lancez le programme au moyen de la cible application que vous prendrez soin de décommenter au préalable dans le fichier CMakeLists.txts. Posez le curseur sur un hamster à traquer et appuyez sur la touche 'T' vous devriez le voir se dessiner avec la petit icône. Appuyez ensuite sur la touche 'O' (qui permet de basculer vers la vue «ECM», et vous devriez voir s'afficher une vue organe (très épurée puisqu'il ne s'agit que d'un fond rose)). La touche 'L' héritée de Application doit permettre de retourner sur la vue «LAB» :
[Video : focus sur un animal et basculement vers la vue organe] |
Dans la vidéo ci-dessus, la touche 'O' est utilisée pour basculer sur la vue interne, la touche 'L' pour revenir à la vue «laboratoire». Vérifiez que :