On s'intéresse à cette page à quelques algorithmes de détection de collision,
qui visent à détecter rapidement des intersections
non-vides entre des «objets».
Les applications sont nombreuses : géométrie algorithmique, simulations physiques, jeux videos, robotique…
Cette page traite de quelques pistes algorithmes en 2D (qu'il serait intéressant d'adapter en 3D).
Considérons deux rectangles dont les côtés sont alignés
avec les axes.
Une formule booléenne simple permet alors de tester s'ils s'intersectent
non(x1min>x2maxoux2min>x1maxouy1min>y2maxouy2min>y2max)
Le x1min>x2max tranduisant par exemple le fait que le premier rectangle est strictement à droite du second.
Dans la figure suivante, vous pouvez cliquer sur un
rectangle pour le déplacer et observer les différentes
configurations possibles.
Le cas des polygones convexes
C'est tout de suite plus délicat.
Notons P1 et P2 les deux polygones. Voici deux algorithmes simples et tentants mais … faux !
Renvoyer vrai si et seulement si au moins un segment de P1 intersecte un segment de P2. (1)
Renvoyer vrai si et seulement si au moins un sommet de P1 est à l'intérieur de P2 ou l'inverse. (2)
Je vous conseille de chercher des contre-exemples à l'aide
d'un papier et d'un crayon - une solution est sur la figure
suivante.
L'implémentation la plus simple à ma connaissance repose sur le théorème suivant - un cas particulier de séparation des convexes.
On considère deux polygones convexes P1 et P2.
Leur intersection est vide si et seulement si il existe un côté C de l'un des polygones P tel que
tous les sommets de P sont dans l'un des demi-plans délimités par la droite prolongeant P (au sens large)
tous les sommets de l'autre polygone sont dans l'autre demi-plan (au sens strict)
Si un polygone convexe sont décrits par la liste de leurs
sommets successifs dans le sens horaire, tous ses sommets
sont alors à gauche de chaque droite reliant des sommets
successifs. Il suffit alors de tester les tous les sommets
de l'autre polygone sont strictement à droite. On obtient
alors un algorithme de complexité O(n1n2) pour tester
l'intersection, où n1 et n2 sont les nombres de
sommets respectifs de P1 et P2.
(À noter qu'il est possible de faire mieux et de généraliser à des polygones non convexes en utilisant des algorithmes par balayage,
qui seront l'objet d'une prochaine page.)
Dans le cas où l'on doit tester des intersections entre de nombreux objets potentiellement éloignés, il est pertinent de précalculer pour chaque objet sa boîte englobante (ou bounding box,
le plus petit rectangle dont les côtés sont alignés avec
les axes et qui contiennent l'objet), et de tester
l'intersection de ses boîtes en utilisant la méthode
décrite pour les rectangles ci-dessus avant de tester
l'intersection des polygones (ce qui devient inutile si
les boîtes englobantes ne s'intersectent pas).
Beaucoup de questions intéressantes peuvent se poser sur l'implémentation efficace d'autres types d'objets, que ce soit en 2D ou en 3D. Cette page recense
de nombreux articles concernant ce sujet.
II-Détection de collision d'un objet avec des obstacles statiques
Considérons un ensemble de N obstacles dans le plan, et
un objet o dont on souhaite savoir s'il intersecte au moins
l'un des obstables.
L'algorithme naïf consiste à tester l'intersection de o
avec chacun des N obstacles. On peut faire mieux que
cela en organisant les obstacles dans une structure
précalculée prenant en compte leur agencement spatial.
L'application suivante permet de comparer les résultats obtenus
par l'algorithme naïf et trois autres algorithmes qui sont
présentés à la suite.
Les Quadtrees (Arbres Quaternaires) sont une méthode classique de partitionnement récursif d'un espace de dimension 2 (ils se généralisent par les Octrees en
dimension 3).
Partant d'un carré à partionner, la racine de
l'arbre représente le carré en entier. On
découpe ce carré initial en quatre carrés, qui
seront représentés par les fils de la racine,
et ainsi de suite récursivement, jusqu'à ce
qu'une profondeur donnée soit atteinte.
Chaque feuille contient une liste de pointeurs
vers les obstacles que son carré intersecte.
Chaque sommet de l'arbre aura zéro ou quatre
fils, et l'on peut décider de construire un
arbre complet, ou au contraire d'arrêter de
subdiviser un carré lorsqu'il n'intersecte
aucun obstacle.
Le test d'intersection avec un objet o
consiste alors comme pour les arbres AABB à partir de la
racine et à n'explorer que les fils représentant des carrés qui intersectent o.
Voir également la page sur les arbres kd,
une autre méthode de partionnement
spatial adaptée à la recherche du plus
proche voisin d'un point parmi un
ensemble de points fixés.
III-Intersection d'objets en déplacement
Méthodes a posteriori
On s'intéresse pour finir à des tests
d'intersection d'un ensemble d'objets
en déplacement entre eux. On
considèrera dans un premier temps des
simulations à temps discret (de type «éléments finis») impliquant
des particules circulaires se
déplaçant dans le plan à vitesse
uniforme et rebondissant sur les bords
de la figure.
Il y a deux nouveaux enjeux par rapport à la section précédente, où l'on testait l'intersection d'un unique objet avec un certain nombre d'obstacles statiques.
La structure permettant les tests
d'intersection peut-elle être mise à jour rapidement pour tenir compte des variations de positions des objets à chaque pas de temps ?
Permet-elle de tester rapidement les intersections entre tous les couples d'objets ?
L'application ci-dessous implémente quelques algorithmes et permet de comparer à vue leur vitesse d'exécution (en gardant en tête que l'affichage lui-même est couteux) :
la méthode naïve, pour laquelle les intersections de tous les couples de particules sont testées à chaque pas de temps
un Quadtree (où à chaque pas de temps, chaque particule est enlevée puis réinsérée dans l'arbre)
une liste des objets triée par abscisses croissantes permettant d'adapter la méthode de balayage local ci-dessus (en utilisant une fenêtre glissante pour détecter les couples de particules qui sont susceptibles de s'intersecter).
Cette dernière méthode est la plus efficace en pratique, car la liste des objets classés par abscisses variant très peu d'un pas de temps au suivant, on peut utiliser par exemple un tri insertion pour la mettre à jour - ce tri étant très efficace sur les listes déjà «presque triées».
Le Quadtree semble moins adapté à ce problème, la suppression puis réinsertion de chaque particule étant trop couteuse.
Modèle physique : Test d'intersection sans déviation
Collisions élastiques
Collisions élastiques avec gravité
Méthode de simulation :Méthode naïve
Liste par abscisses croissantes
Quad Tree
(
)
A Priori
Choc élastique
Une fois les collisions détectées (on parle de détection a posteriori), on peut utiliser un modèle physique, par exemple de choc élastique pour dévier les particules.
C'est ce qui est fait dans l'application ci-dessus (avec des chocs élastiques de type «boules de billard» - où l'interaction entre deux particules se traduit par des forces contenues dans la droite passant par leur centre - mais que l'on pourrait adapter pour simuler d'autres types d'interactions).
On peut par exemple y observer la diffusion de particules de deux couleurs, ou le trajet de la particule orange individualisée. On peut également rajouter une force gravitationnelle, pour observer la répartition des particules selon l'altitude, etc.
Le problème des approches a posteri et que la collision est détectée trop tard, ce qui peut entraîner des aberrations (ici les couples de particules qui restent «collées» entre elles). On peut même rater intégralement une collision si une particule rapide en «traverse» une autre entre deux pas de temps successifs.
Les approches a posteriori permettent cependant de mettre en place plus facilement ce type de simulations que les méthodes a priori, qui ne fonctionnent pas avec un pas de temps mais consistent à prévoir le moment exact de la prochaine collision à partir de l'ensemble des paramètres (position et vitesse) des particules, et à faire évoluer le système d'une collision à la suivante.
Méthode a priori
On se replace dans le cas sans gravité.
Connaissant les positions et
vitesses de l'ensemble des
particules, on peut analytiquement
déterminer la prochaine collision
de chacune d'entre elles avec une
paroi ou avec une autre
particule. C'est très simple pour
une paroi, et étant donné deux
particules de positions
respectives p_1 et p_2 et de
vitesses v_1 et v_2, et de
rayon r, on peut déterminer s'il existe le
plus petit temps t>0 tel que
\|(p_1+tv_1)-(p_2+tv_2)\| = 2r -
il s'agit en fait de résoudre une
équation de degré 2. (Si l'on change le modèle physique, on risque de tomber sur une équation qui n'est pas résoluble analytiquement - et l'on peut donc avoir recours à des méthodes approchées).
On cherche le minimum de tous ces
temps pour l'ensemble des
particules, et l'on obtient donc
le temps du prochain «évènement»
se produisant pour l'ensemble des
particules.
On peut alors «avancer»
directement la simulation jusqu'à ce temps - qui
est le temps exact t_{min} de la prochaine
collision - sans utiliser une
méthode de type éléments finis.
La collision est donc gérée au temps exact où elle se produit, ce qui évite les problèmes des approches a posteriori.
Les calculs étant lourds, on gagne à stocker le prochain évènement pour chaque boule dans une file de priorité - de façon à pouvoir extraire rapidement la ou les boules b_1 (et b_2) concernées par la prochaine collision. Après une collision, on recalcule uniquement les temps de prochaine collision de b_1 (et b_2) et des particules dont le prochain évènement était une collision avec b_1 (ou b_2) - la priorité des autres particules étant simplement diminuée de t_{min}