Licence Informatique 2017-2018
II2D



TP 04 : Evolutions (segments/interactions)

1  Segments

On intègre les segments dans les obstacles. On procède de manière identique au cercle.

Question 1.

  1. Faire une classe Segment (attributs a et b pour les extrémités).
  2. Faire l’affichage d’un segment (faire une méthode draw dans la classe Segment) : pour tracer un segment il faut tracer un path en utilisant moveTo et lineTo; voir un exemple ici sur le MDN
  3. Faire la méthode Segment.intersect (cf la méthode des signes du cours; n’importe quel point du segment convient comme point de collision)
  4. Y a t’il quelque chose de particulier à faire pour la réponse ?

Testez

2  Restitution

Question 2. Gérez un coefficient de restitution pour chaque obstacle (i.e. dans Circle et Segment). Il faut l’intégrer comme attribut d’un obstacle, comme information de collision et dans le calcul de la méthode Engine.impulse.

Testez en fixant des coefficients de restitution différents.

3  Sélection d’un obstacle avec la souris

Le but est de pouvoir déplacer un obstacle avec la souris. Il y a 2 phases principales : la sélection de l’obstacle, puis la manipulation de cet obstacle (son déplacement).

Question 3. Vous avez déjà répondu aux messages de la souris 'mousedown' et 'mousemove' dans le TP01. Modifiez la méthode qui répond au 'mousedown' pour appeler une méthode Engine.selection(x,y) (elle sera à compléter par la suite). Modifiez la méthode qui répond au 'mousemove' pour appeler une méthode Engine.move(x,y) lorsque le bouton de la souris est maintenu (qui sera également à compléter dans la suite). Vous pouvez, pour le moment, mettre en commentaire la gestion du mouvement de la source faite au TP 01.

Question 4. Pour la manipulation, il faudra mémoriser une référence sur l’obstacle sélectionné : intégrez cet attribut à la classe Engine. Cet attribut sera null quand il n’y a aucun obstacle sélectionné, et sera affecté à la référence d’un obstacle quand il y a sélection.

Pour le moment, dans Engine.selection(), affectez l’obstacle sélectionné au premier obstacle de la liste des obstacles. Assurez alors que l’obstacle sélectionné change de couleur dans Engine.selection (impose de faire un attribut color dans Circle et Segment sans oublier de l’exploiter dans leur draw respectif).

Testez : lorsque vous cliquez le premier obstacle doit changer de couleur (on pourrait remettre sa couleur par défaut lorsqu’on relache le bouton, mais ce n’est pas nécessaire pour la suite).

Nous allons, dans les questions suivantes, sélectionner l’obstacle le plus proche du pointeur de souris quand on clique sur le bouton. On fixera un seuil (une distance maximale ) pour lequel aucun obstacle ne sera sélectionné (si la souris est trop loin de tout obstacle, aucun ne sera sélectionné).

Question 5. Faites une méthode Cercle.distance(un_point) qui donne la distance d’un point (x,y) au contour du cercle. Vous pouvez vous appuyer sur le schéma suivant pour déterminer le calcul.

Testez en affichant (dans la console) la distance entre 1 de vos obstacles cercle et la souris lors d’un click.

Question 6. Faire de même pour le segment : il faut déterminer dans quelle zone se trouve le point m(x,y) dont on veut calculer la distance au segment (cf schéma) :

Question 7. Pour sélectionner le plus proche : dans ObstacleManager, faire une méthode closest(un_point) qui renvoie la référence de l’obstacle le plus proche de un_point et exploitez dans Engine.selection(). Testez.

Question 8. Intégrez un seuil de sélection (il y a sélection si l’obstacle est suffisamment proche de la souris) : ObstacleManager.closest renvoie null si on est au-delà de ce seuil. Testez.

Mouvement d’un obstacle à la souris

Question 9. Dans Engine.move(x,y), appelez une méthode move(x,y) pour l’obstacle sélectionné : pour le cercle, déplacez son centre en (x,y) et pour le segment, déplacez une de ses extrémités en (x,y) (pour le moment). Testez : vous allez constater plusieurs défauts :

  1. le mouvement à la souris n’est pas intuitif du fait du saut au premier mouvement (par exemple le centre d’un cercle sélectionné est brutalement positionné sur la souris).
  2. dès qu’on bouge un obstacle, les particules passent aisément "à travers" : lors du déplacement de l’obstacle, les particules proches passent de l’autre coté sans détection.

Question 10. Déplacez l’obstacle sélectionné (i.e. le centre du cercle ou l’extrémité du segment) en lui ajoutant le déplacement de la souris : cela nécessite de gérer des mousex_old, mousey_old pour la souris; on passera à move les incréments mousex-mousex_old et mousey-mousey_old. Testez.

Question 11. Passons à une interaction plus souple du segment : pour aller au plus direct, lors du calcul de Segment.distance(p) on mémorise dans quelle zone on est (attribut de Segment qui indique si on se trouve dans la zone a, zone b ou zone line), et on teste cet attribut lors de Segment.move(p). Testez.

Remarquez que lorsque le curseur de souris est dans la zone line mais très proche d’une extrémité, c’est tout le segment qui se déplace. On peut éventuellement améliorer cela en contractant un peu la zone line.

Question 12. Enfin, préoccupons nous de réduire le passage à travers les obstacles lors de leur déplacement. Il s’agit, lors de la collision, de considérer que l’obstacle a bougé d’une ancienne position à une nouvelle position : intégrez la mémorisation de l’ancienne position dans le segment (attributs old_a et old_b par exemple) et le cercle (attribut old_c). Prenez soin de bien gérer ces nouveaux attributs lors du déplacement à la souris.

Question 13. Pour exploiter ce mouvement d’obstacle lors de la collision (i.e. dans Engine.solveCollision), on considère que l’ancienne position de la particule (attribut p.oldPosition) doit être testée avec l’ancienne position de l’obstacle. Plutôt que de tout modifier, on va seulement modifier l’ancienne position pour qu’elle corresponde à sa position relative à la nouvelle position de l’obstacle (i.e. on déplace l’ancienne particule en considérant l’obstacle fixe).

Corrigez cette ancienne position de la particule relativement à l’obstacle considéré (attention : ne modifiez pas directement p.oldPosition car il est utilisé pour chaque obstacle : créez une nouvelle variable). Par exemple, pour le cercle, si son centre s’est déplacé de old_c à c, il suffit de déplacer p.oldPosition de c-old_c.

Testez (remarque : il y aura toujours des particules qui ignoreront un obstacle mobile car la réponse se fait toujours avec un obstacle fixe).

4  Pour finir

Question 14. Codez une scène (initialisations d’obstacles et des générateurs : soyez imaginatifs sur les paramètres) pour illustrer le tout.

Question 15. (Ré)intégrez l’interaction sur un/des générateurs : par exemple si aucun obstacle n’est sélectionné lors du click, on sélectionne le générateur le plus proche qu’on peut alors déplacer.

Question 16. Bonus :

  1. On peut améliorer un peu la réaction au mouvement d’un obstacle en tenant compte de la vitesse de l’obstacle : on traduit approximativement le fait que plus l’obstacle s’est déplacé et plus la vitesse d’impulsion sera élevée. Traduisez ce principe.
  2. On peut améliorer un peu les cas de collisions multiples en appliquant les corrections de position et de vitesse uniquement à la fin de la résolution de toutes les collisions en cumulant les corrections multiples (et non pas au fur et à mesure).
  3. Si on augmente le rayon des particules, vous constatez clairement la collision qui est faite au "centre" de la particule (i.e. la particule se superpose aux obstacles) : on peut gérer cela en modifiant l’obstacle (par exemple lors de la détection de la collision avec un cercle : si la particule est à l’extérieur du cercle, on augmente le rayon du cercle avec le rayon de la particule).

Ce document a été traduit de LATEX par HEVEA