Buts: Modéliser les ondes émises par les lézards lors de leurs déplacements
Nous adopterons le modèle simplifié suivant pour les ondes émises par les lézard :
I(t) = E0 * exp(-r(t)/µ) / (2 * PI * r(t)) = E0 * exp(-(t*v+R0)/µ) / (2 * PI * r(t))(car l'énergie se répartit uniformément sur une cercle de périmètre 2 * PI * r(t))
[Video : onde se «brisant» sur des obstacles] |
Pour pouvoir dessiner des fragments d'ondes, il est judicieux de stocker ces derniers dans un attribut modélisé comme un ensemble d'arcs; un arc étant caractérisé par un angle de début et un angle de fin. Au départ, il n'y aurait qu'un seul arc dans cet ensemble, allant de -PI à PI. Cet arc pourra ensuite être fragmenté en plusieurs sous-arcs si l'onde croise un obstacle (ce que nous traiterons un peu plus bas). La constante PI est fournie dans le fichier Utility/Constants.hpp.
Il vous est donc demandé maintenant de coder une classe Wave caractérisée par :
Pour dessiner l'onde, il faudra dessiner les arcs qui la constituent. La méthode buildArc fournie dans Utility[.hpp][cpp] pourra être utilisée. L'épaisseur du trait pourra être proportionnel à l'intensité de l'onde selon un facteur getAppConfig().wave_intensity_thickness_ratio.
[Question Q4.1] Quel liens d'héritage vous semble t-il pertinent d'établir lors du codage de la classe Wave ?
[Question Q4.2] Comment comptabilisez-vous le temps écoulé depuis le début de la propagation de l'onde pour faire évoluer le rayon de celle-ci ? Répondez à ces questions dans votre fichier fichier REPONSES et adaptez votre code en conséquence.
Vous considérerez que les ondes appartiennent à l'environnement et ajouterez à la classe Environment une méthode addWave(Wave*).
Une onde dont l'intensité est trop faible (en dessous de getAppConfig().wave_intensity_threshold) doit disparaître de l'environnement.
[Question Q4.3] Quelles retouches devez vous faire à la Environment pour prendre en compte la présence des ondes et les voir se propager et se dessiner ? Répondez à cette question dans votre fichier fichier REPONSES.
Le test fourni WaveTest.cpp que vous pouvez lancer au moyen de la cible waveTest permet de tester la propagation des ondes et leur représentation graphique. Ce test est programmé de sorte à ce qu'un clic gauche avec la souris crée un onde. Cette dernière doit alors se propager selon le modèle de la petite vidéo suivante (redémarrez-là au besoin):
[Video : propagation d'ondes] |
Il est question maintenant de simuler le fait que l'onde peut se fragmenter sur des obstacles. La mise à jour de l'onde doit donc en plus se préoccuper de l'éventualité d'une collision avec un obstacle. Nous considérerons pour simplifier que tous les obstacles sont des Collider.
Définissez Rock comme une sous classe de Collider représentant un obstacle solide (libre à vous d'étendre cette conception et de coder une hiérarchie d'obstacles solides dont dérivera Rock). Un rocher est un objet dessinable doté d'une orientation (angle). Son constructeur prendra en paramètre sa position (un Vec2d) et tirera au hasard son orientation entre -PI et PI au moyen de la fonction aléatoire uniform (fournie dans Random/Random.[hpp/cpp]). Le rayon d'un Rock sera tiré au hasard entre getAppConfig().simulation_world_size/50 et 2*getAppConfig().simulation_world_size/50, mais vaudra au minimum 1.
[Question Q4.4] Quelle(s) autre(s) modification(s) devez vous apporter à la classe Environment suite à l'ajout de la méthode newObstacle?
Modifiez ensuite la méthode de mise à jour de Wave de sorte à ce qu'elle intègre aussi la détection de collision avec des obstacles de l'environnement. Une collision est détectée avec un obstacle de l'environnement si ce dernier est en collision avec l'onde ou est à l'intérieur de celle-ci (en tant que Collider). Pour simplifier, on ne tiendra compte que des obstacles dont la position se trouve dans un des arcs de l'onde :
![]() | ← le second obstacle ne fragmente pas l'onde en plus petits arcs même si un des arcs le touche un peu (il faut que le centre de l'obstacle soit dans un arc pour qu'il ait effet sur lui) |
Vous fragmenterez l'arc en vous inspirant des indications du schéma suivant:
![]() | ← l'angle α qui permet de scinder l'arc bleu en deux arcs rouges peut se calculer selon la formule 2 * std::atan2(r, R + r) (le cercle dans la figure représente l'obstacle). R est le rayon de l'onde et r celui de l'obstacle. |
Pour tester cette partie, vous pourrez à nouveau utiliser WaveTest.cpp que vous pouvez lancer au moyen de la commande waveTest.
Créez plusieurs obstacles et ondes, vous devriez pouvoir observer la fragmentation de vos ondes selon l'exemple suivant:
[Video : propagation d'ondes avec obstacles] |
Un lézard émetteur d'ondes (classe WaveLizard) est une Lizard qui a en plus la particularité d'émettre des ondes toutes les 1.0/getAppConfig().wave_lizard_frequency secondes.
Les autres caractéristiques de l'onde seront les suivantes :
Pour être compatible avec les fichiers de tests fourni, le WaveLizard aura des constructeurs aux signatures analogues à ceux codés pour Lizard.
Vous pouvez maintenant commencer à utiliser le programme principal correpondant à l'ensemble de votre projet : FinalApplication.cpp. Ce programme vous permet de tester le nouveau type de lézards ainsi créées.
Les modalités de lancement du test seront les suivantes :
Vous devriez voir une lézard se déplaçant au hasard en émettant des ondes à une fréquence régulière, tel que le montre la vidéo suivante :
[Video : lézards émetteur d'ondes] |