Innanzitutto, nel caso dei rettangoli allineati agli assi, la risposta di Kevin Reid è la migliore e l'algoritmo è il più veloce.
In secondo luogo, per forme semplici, utilizzare le velocità relative (come mostrato di seguito) e il teorema degli assi di separazione per il rilevamento delle collisioni. Essa vi dirà se una collisione accade nel caso di movimento lineare (senza rotazione). E se c'è rotazione, è necessario un piccolo timestep per essere preciso. Ora, per rispondere alla domanda:
Come dire nel caso generale se due forme convesse si intersecano?
Ti darò un algoritmo che funziona per tutte le forme convesse e non solo per gli esagoni.
Supponiamo che X e Y siano due forme convesse. Si intersecano se e solo se hanno un punto in comune, cioè c'è un punto x ∈ X e un punto y ∈ Y tale che x = y . Se consideri lo spazio come uno spazio vettoriale, ciò equivale a dire x - y = 0 . E ora arriviamo a questo business di Minkowski:
La somma Minkowski di X e Y è l'insieme di tutte le x + y per x ∈ X e y ∈ Y .
Un esempio per X e Y
X, Y e la loro somma di Minkowski, X + Y
Supponendo che (-Y) sia l'insieme di tutti -y per y ∈ Y , quindi dato il paragrafo precedente, X e Y si intersecano se e solo se X + (-Y) contiene 0 , ovvero l'origine .
Nota laterale: perché scrivo X + (-Y) invece di X - Y ? Bene, perché in matematica esiste un'operazione chiamata la differenza di Minkowski di A e B che a volte è scritta X - Y ma non ha nulla a che fare con l'insieme di tutti x - y per x ∈ X e y ∈ Y (il vero Minkowski la differenza è un po 'più complessa).
Quindi vorremmo calcolare la somma di Minkowski di X e -Y e scoprire se contiene l'origine. L'origine non è speciale rispetto a qualsiasi altro punto, in modo che per scoprire se l'origine si trova in un determinato dominio, utilizziamo un algoritmo che potrebbe dirci se un determinato punto appartiene a quel dominio.
La somma Minkowski di X e Y ha una proprietà fresco, che è che se X e Y sono convesse, allora X + Y è troppo. E scoprire se un punto appartiene a un insieme convesso è molto più facile che se quell'insieme non fosse (noto per essere) convesso.
Non possiamo calcolare tutti i x - y per x ∈ X e Y ∈ Y , perché ci sono un'infinità di tali punti x ed y , così eventualmente, poiché X , Y e X + Y sono convesse, è sufficiente utilizzare i punti "più esterni" che definiscono le forme X e Y , che sono i loro vertici, e otterremo i punti più esterni di X + Y , e anche alcuni altri.
Questi punti aggiuntivi sono "circondati" da quelli più esterni di X + Y in modo che non contribuiscano a definire la forma convessa appena ottenuta. Diciamo che non definiscono lo " scafo convesso " dell'insieme di punti. Quindi quello che facciamo è che ci liberiamo di loro in preparazione dell'algoritmo finale che ci dice se l'origine è all'interno dello scafo convesso.
Lo scafo convesso di X + Y. Abbiamo rimosso i vertici "interni".
Quindi otteniamo
Un primo, ingenuo algoritmo
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
I loop ovviamente hanno complessità O (mn) dove m e n sono il numero di vertici di ciascuna forma. Il minkoswki
set contiene al massimo elementi mn . L' convexHull
algoritmo ha una complessità che dipende dall'algoritmo che hai usato e puoi mirare a O (k log (k)) dove k è la dimensione dell'insieme di punti, quindi nel nostro caso otteniamo O (mn log (mn) ) . L' contains
algoritmo ha una complessità che è lineare con il numero di spigoli (in 2D) o facce (in 3D) dello scafo convesso, quindi dipende davvero dalle forme di partenza, ma non sarà maggiore di O (mn) .
Ti lascerò google per l' contains
algoritmo per le forme convesse, è piuttosto comune. Posso metterlo qui se ho tempo.
Ma stiamo facendo il rilevamento delle collisioni, quindi possiamo ottimizzarlo molto
Inizialmente avevamo due corpi A e B che si muovevano senza rotazione durante un timestep dt (da quello che posso dire guardando le tue foto). Chiamiamo v A e v B le rispettive velocità di A e B , che sono costanti durante il nostro timestep di durata dt . Otteniamo quanto segue:
e, come fai notare nelle tue immagini, questi corpi attraversano aree (o volumi, in 3D) mentre si muovono:
e finiscono come A ' e B' dopo il timestep.
Per applicare il nostro algoritmo ingenuo qui, dovremmo solo calcolare i volumi spazzati. Ma non lo stiamo facendo.
Nel frame di riferimento di B , B non si muove (duh!). E A ha una certa velocità rispetto a B che si ottiene calcolando v A - v B (si può fare il contrario, calcolare la velocità relativa di B nel frame di riferimento di A ).
Da sinistra a destra: velocità nel quadro di riferimento di base; velocità relative; calcolo delle velocità relative.
Per quanto riguarda B come immobile nel suo quadro di riferimento, devi solo calcolare il volume che A scorre mentre si muove durante dt con la sua velocità relativa v A - v B .
Ciò riduce il numero di vertici da utilizzare nel calcolo della somma di Minkowski (a volte notevolmente).
Un'altra possibile ottimizzazione è nel punto in cui si calcola il volume spazzato da uno dei corpi, diciamo A. Non è necessario tradurre tutti i vertici che compongono A. Solo quelli che appartengono ai bordi (facce in 3D) il cui "faccia" normale esterna la direzione dello spazzamento. Sicuramente lo avevi notato già quando hai calcolato le aree spazzate per i quadrati. Puoi dire se un normale è verso la direzione di spazzamento usando il suo prodotto punto con la direzione di spazzamento, che deve essere positivo.
L'ultima ottimizzazione, che non ha nulla a che fare con la tua domanda riguardo alle intersezioni, è davvero utile nel nostro caso. Utilizza quelle velocità relative che abbiamo menzionato e il cosiddetto metodo dell'asse di separazione. Sicuramente lo sai già.
Supponiamo di conoscere i raggi di A e B rispetto ai loro centri di massa (vale a dire, la distanza tra il centro di massa e il vertice più lontano da esso), in questo modo:
Una collisione può verificarsi solo se è possibile che il cerchio di delimitazione di un incontro che di B . Vediamo qui che non lo farà, e il modo di dire al computer che è di calcolare la distanza da C B a Ho come nella seguente immagine e assicurarsi che sia più grande della somma dei raggi di A e B . Se è più grande, nessuna collisione. Se è più piccolo, quindi la collisione.
Questo non funziona molto bene con forme piuttosto lunghe, ma nel caso di quadrati o altre forme simili, è un'ottima euristica escludere la collisione .
Il teorema dell'asse di separazione applicato a B e il volume spazzato da A , tuttavia, indicano se si verifica la collisione. La complessità dell'algoritmo associato è lineare con la somma dei numeri di vertici di ciascuna forma convessa, ma è meno magico quando arriva il momento di gestire effettivamente la collisione.
Il nostro nuovo e migliore algoritmo che utilizza le intersezioni per aiutare a rilevare le collisioni, ma non è ancora buono come il teorema dell'asse di separazione per dire effettivamente se si verifica una collisione
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}