Problema di rilevamento delle collisioni Circle-Line


11

Attualmente sto sviluppando un clone di breakout e ho colpito un blocco stradale per far funzionare correttamente il rilevamento delle collisioni tra una palla (cerchio) e un mattone (poligono convesso). Sto usando un test di rilevamento delle collisioni Circle-Line in cui ogni linea rappresenta e spigola sul mattone poligonale convesso.

Per la maggior parte del tempo il test Circle-Line funziona correttamente e i punti di collisione vengono risolti correttamente.

Il rilevamento delle collisioni funziona correttamente.

Tuttavia, a volte il mio codice di rilevamento delle collisioni restituisce falso a causa di un discriminante negativo quando la palla si interseca effettivamente con il mattone.

Rilevamento collisioni non riuscito.

Sono consapevole dell'inefficienza di questo metodo e sto usando delle scatole di delimitazione allineate agli assi per ridurre il numero di mattoni testati. La mia principale preoccupazione è se ci sono dei bug matematici nel mio codice qui sotto.

/* 
 * from and to are points at the start and end of the convex polygons edge.
 * This function is called for every edge in the convex polygon until a
 * collision is detected. 
 */

bool circleLineCollision(Vec2f from, Vec2f to)
{
    Vec2f lFrom, lTo, lLine;
    Vec2f line, normal;
    Vec2f intersectPt1, intersectPt2;
    float a, b, c, disc, sqrt_disc, u, v, nn, vn;
    bool one = false, two = false;

    // set line vectors
    lFrom = from - ball.circle.centre;      // localised
    lTo = to - ball.circle.centre;          // localised
    lLine = lFrom - lTo;                    // localised
    line = from - to;

    // calculate a, b & c values
    a = lLine.dot(lLine);
    b = 2 * (lLine.dot(lFrom));
    c = (lFrom.dot(lFrom)) - (ball.circle.radius * ball.circle.radius);

    // discriminant
    disc = (b * b) - (4 * a * c);

    if (disc < 0.0f)
    {
        // no intersections
        return false;
    }
    else if (disc == 0.0f)
    {
        // one intersection
        u = -b / (2 * a);

        intersectPt1 = from + (lLine.scale(u));
        one = pointOnLine(intersectPt1, from, to);

        if (!one)
            return false;
        return true;
    }
    else
    {
        // two intersections
        sqrt_disc = sqrt(disc);
        u = (-b + sqrt_disc) / (2 * a);
        v = (-b - sqrt_disc) / (2 * a);
        intersectPt1 = from + (lLine.scale(u));
        intersectPt2 = from + (lLine.scale(v));

        one = pointOnLine(intersectPt1, from, to);
        two = pointOnLine(intersectPt2, from, to);

        if (!one && !two)
            return false;
        return true;
    }
}

bool pointOnLine(Vec2f p, Vec2f from, Vec2f to)
{
    if (p.x >= min(from.x, to.x) && p.x <= max(from.x, to.x) && 
        p.y >= min(from.y, to.y) && p.y <= max(from.y, to.y))
        return true;
    return false;
}

Non riesco a trovare alcuna differenza tra lLine e line ...
FxIII

Il test pointOnLine può essere semplificato ed eseguito prima di calcolare il punto effettivo.
FxIII,

come viene calcolato sqrt_disc?
FxIII,

Mi dispiace FxIII. Devo essermi un po 'confuso quando stavo localizzando i miei vettori. Non mi ero reso conto che i vettori sarebbero stati uguali quando sarebbero stati sottratti l'uno dall'altro. Stavo ripulendo il mio codice un po 'prima di pubblicare e ho dimenticato di rimetterlo sqrt_disc = sqrt(disc);. Grazie mille per la risposta che segue mi ha aiutato molto.
jazzdawg,

Risposte:


20

Il segmento che va da A a B può essere calcolato come

P (t) = A + D · t dove D è B - A et va da 0 a 1

Ora il cerchio è centrato sull'origine (spostare A e B se necessario per posizionare il centro sull'origine) e ha il raggio r .

Hai un'intersezione se per alcuni t ottieni che la P ha la stessa lunghezza di r o, equivalentemente, che la lunghezza di P al quadrato è equivalente a

La lunghezza al quadrato di un vettore si ottiene facendo da solo il prodotto punto di un vettore (questo è così vero che se si trova un'operazione adatta per il prodotto punto può definire un nuovo e coerente concetto di lunghezza)

P · P = ( A + D · t) · ( A + D · t) =

A · A + 2 A · D t + D · D

Vogliamo trovare per quale t otteniamo P · P = r², quindi finiamo per chiederci quando

A · A + 2 A · D t + D · D t² = r²

o quando

D · D t² + 2 A · D t + A · A -r² = 0

questa è la famosissima equazione quadratica

at² + bt + c = 0

con

a = D · D ; b = 2 A · D ec = A · A -r²

Dobbiamo verificare se il determinante b² - 4ac è positivo e quindi troviamo 2 valori di t che ci danno i punti di intersezione P (t).

t deve essere tra 0 e 1 altrimenti abbiamo trovato soluzioni che si trovano sulla linea che passa attraverso A e B ma che sono prima di A o dopo B

[MODIFICARE]

Dato che altre domande possono trovare un aiuto per questa risposta, ho deciso di provare a semplificare il ragionamento in questa modifica usando alcune immagini. condizione iniziale Questa è la condizione iniziale. Ora concentrati sul segmento A_B

Segmento che va da A a B

D è il vettore che sposta A in B, quindi se t è compreso tra 0 e 1, D · t è una "frazione propria" di D, quindi il punto A + D · t si trova nel segmento A_B : i punti marroni si presentano quando t è tra 0 e 1 e quello verde scuro è quando t> 1.

Ora possiamo semplificare le cose se spostiamo il centro del cerchio nell'Origine. Questo può essere sempre fatto perché si tratta semplicemente di un cambiamento del sistema di coordinate che preserva geometria, angoli, intersezione, misure ecc.

cerchio che si sposta al centro

Ora abbiamo un modo semplice per calcolare la lunghezza di P quando t varia e dire per quale t P attraversa i confini del cerchio.

Esempi

Come vedi P ' una lunghezza maggiore di r mentre P " è minore di r. Poiché sia ​​la lunghezza del vettore che r sono numeri positivi, la relazione dell'ordine di essere maggiore o minore di quanto viene conservato è calcoliamo la relazione tra le lunghezze quadrato e raggio quadrato P * 1 e P * 2 sono il punto che rende | P | ² uguale a r²

Come menzionato nella sezione pre-modifica, arriviamo a ottenere un'equazione quadratica dove t è la nostra variabile. Come è noto, i valori della soluzione di t vanno dal caso in cui t è una coppia di numeri complessi - ciò significa nessuna intersezione; il caso in cui t sono due soluzioni uguali - ciò significa che esiste un'intersezione; il caso in cui ci sono due soluzioni distinte, ciò significa che ci sono due intersezioni.

Il discriminante viene utilizzato per discriminare la condizione precedente e viene effettuato un test di validità su t per vedere se si tratta di un incrocio valido ma al di fuori del nostro segmento - vale a dire che la soluzione t deve essere reale e tra 0 e 1 per essere considerata un incrocio corretto che cade nel segmento A_B


3
Questo è l'algoritmo giusto da usare. Una descrizione davvero buona di come funziona può essere trovata in Real Time Rendering Third Edition , pagine da 787 a 791. Se la trovi in ​​una biblioteca, vale la pena dare un'occhiata.
Darcy Rayner,

4
Con l'ottavo voto a questa risposta, ho raggiunto 2k punti reputazione. Apprezzo molto la fiducia che hai riposto in me. Questo è sia un riconoscimento dei miei sforzi sia uno stimolo a continuare a fare del mio meglio per produrre la risposta della più alta qualità possibile. Grazie
FxIII

Aspetta, questo account per i due casi angolari è corretto? Ad esempio, un cerchio può attraversare il piano definito dalla linea al di fuori di t0 <= t <= t1, ma colpire i punti finali del segmento di linea un po 'più tardi. Devi controllare la distanza minima tra i punti finali della linea e il percorso dei cerchi. Se quella distanza è inferiore al raggio del cerchio, allora la linea è stata colpita.
Darcy Rayner,

@DarcyRayner intendi il caso in cui entrambi i punti si trovano all'interno dell'area del cerchio?
FxIII,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.