Come posso verificare in modo efficiente se un punto si trova all'interno di un rettangolo ruotato?


11

In parte per motivi di ottimizzazione, in parte a fini di apprendimento, oserò chiedere: come posso verificare in modo più efficace se un punto 2D si Ptrova all'interno di un rettangolo ruotato 2D XYZW, usando C # o C ++?

Attualmente, quello che sto facendo è usare un algoritmo "punto nel triangolo" trovato nel libro Real Time Collision Detection , ed eseguirlo due volte (per i due triangoli che compongono il rettangolo, diciamo XYZ e XZW):

bool PointInTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
 // Compute vectors        
 Vector2 v0 = C - A;
 Vector2 v1 = B - A;
 Vector2 v2 = P - A;

 // Compute dot products
 float dot00 = Vector2.Dot(v0, v0);
 float dot01 = Vector2.Dot(v0, v1);
 float dot02 = Vector2.Dot(v0, v2);
 float dot11 = Vector2.Dot(v1, v1);
 float dot12 = Vector2.Dot(v1, v2);

 // Compute barycentric coordinates
 float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
 float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
 float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

 // Check if point is in triangle
 if(u >= 0 && v >= 0 && (u + v) < 1)
    { return true; } else { return false; }
}


bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
 if(PointInTriangle(X,Y,Z,P)) return true;
 if(PointInTriangle(X,Z,W,P)) return true;
 return false;
}

Tuttavia, ho la sensazione che potrebbe esserci un modo più pulito e veloce. In particolare, per ridurre il numero di operazioni matematiche.


Hai molti punti o hai molti rettangoli? Questa è la prima domanda che dovresti porti prima di provare a ottimizzare un compito così piccolo.
sam hocevar,

Buon punto. Avrò un numero molto alto di punti, ma anche più rettangoli da controllare.
Louis15,

Domanda correlata su come trovare la distanza di un punto da un rettangolo ruotato . Questo è un caso degenerato (verificando solo quando la distanza è 0). Naturalmente, ci saranno ottimizzazioni che si applicano qui che non ci sono.
Anko,

Hai mai considerato di ruotare il punto nel riquadro di riferimento del rettangolo?
Richard Tingle,

@RichardTingle In realtà non l'ho fatto all'inizio. Più tardi l'ho fatto, perché penso che si riferisca a una delle risposte fornite di seguito. Ma solo per chiarire: in quello che stai suggerendo, dopo aver ruotato il punto sulla cornice di riferimento dei rettangoli, allora si dovrebbe verificare l'inclusione solo attraverso confronti logici tra max.x, min.x, ecc.?
Louis15,

Risposte:


2

Un'ottimizzazione semplice e diretta sarebbe quella di modificare la condizione finale in PointInTriangle:

bool PointInRectangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) {
  ...
  if(u >= 0 && v >= 0 && u <= 1 && v <= 1)
      { return true; } else { return false; }
  }
}

il codice era PointInRectanglegià praticamente , la condizione (u + v) < 1era lì per verificare se non si trova nel "secondo" triangolo del rettangolo.

In alternativa, potresti anche fare un isLefttest (primo esempio di codice a pagina, anche ampiamente spiegato) quattro volte e verificare che tutti restituiscano risultati con lo stesso segno (che dipende dal fatto che i punti siano stati assegnati in senso orario o antiorario) per il punto di essere dentro. Funziona anche con qualsiasi altro poligono convesso.

float isLeft( Point P0, Point P1, Point P2 )
{
    return ( (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y) );
}
bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
    return (isLeft(X, Y, P) > 0 && isLeft(Y, Z, P) > 0 && isLeft(Z, W, P) > 0 && isLeft(W, X, p) > 0);
}

Superba. Non so se mi piace di più il tuo suggerimento, che è davvero più veloce e molto più elegante del mio, o se mi piace di più che hai notato che il mio codice PointInTri potrebbe facilmente diventare un PointInRec! Grazie
Louis15,

+1 per il isLeftmetodo. Non richiede funzioni di trigger (come Vector2.Dotfa), che accelera molto le cose.
Anko,

A proposito, non è stato possibile modificare il codice (non testarlo; non avere come in questo computer), includendo isLeft direttamente nella funzione principale e sostituendo gli operatori "&&" con "||" attraverso la logica inversa? public static bool PointInRectangle(Vector2 P, Vector2 X, Vector2 Y, Vector2 Z, Vector2 W) { return !(( (Y.x - X.x) * (P.y - X.y) - (P.x - X.x) * (Y.y - X.y) ) < 0 || ( (Z.x - Y.x) * (P.y - Y.y) - (P.x - Y.x) * (Z.y - Y.y) ) < 0 || ( (W.x - Z.x) * (P.y - Z.y) - (P.x - Z.x) * (W.y - Z.y) ) < 0 || ( (X.x - W.x) * (P.y - W.y) - (P.x - W.x) * (X.y - W.y) ) < 0 ); }
Louis15,

1
@ Louis15 Non credo che sia necessario - sia && sia || interromperà l'esecuzione di ulteriori dichiarazioni se viene trovato un negativo / positivo (o c'era un altro motivo?). Dichiarare isLeftcome in linea il compilatore farà qualcosa di simile per te (e probabilmente meglio di così potresti, perché gli ingegneri che scrivono il compilatore conoscevano meglio le CPU, scegliendo qualsiasi opzione sia la più veloce) rendendo il tuo codice più leggibile con lo stesso o migliore effetto.
Wondra,

8

Modifica: il commento dei PO è stato scettico sull'efficienza del controllo del limite circolare negativo suggerito per migliorare l'algoritmo, al fine di verificare se un punto 2D arbitrario si trova all'interno di un rettangolo ruotato e / o mobile. Giocherellando un po 'sul mio motore di gioco 2D (OpenGL / C ++), integra la mia risposta fornendo un benchmark delle prestazioni del mio algoritmo rispetto agli attuali algoritmi (e variazioni) di point-in-rectangle-check degli OP.

Inizialmente ho suggerito di lasciare l'algoritmo in atto (dato che è quasi ottimale), ma di semplificare attraverso la semplice logica di gioco: (1) usando un cerchio pre-elaborato attorno al rettangolo originale; (2) eseguire un controllo della distanza e se il punto si trova all'interno del cerchio dato; (3) utilizzare gli OP o altri algoritmi semplici (consiglio l'algoritmo isLeft come indicato in un'altra risposta). La logica alla base del mio suggerimento è che verificare se un punto si trova all'interno di un cerchio è considerevolmente più efficiente di un controllo al contorno di un rettangolo ruotato o di qualsiasi altro poligono.

Il mio scenario iniziale per un test di riferimento è quello di eseguire un gran numero di punti che appaiono e scompaiono (la cui posizione cambia in ogni ciclo di gioco) in uno spazio limitato che sarà riempito con circa 20 quadrati rotanti / mobili. Ho pubblicato un video ( link di YouTube ) a scopo illustrativo. Notare i parametri: numero di punti che appaiono casualmente, numero o rettangoli. Farò un benchmark con i seguenti parametri:

OFF : algoritmo semplice come fornito dall'OP senza controlli negativi al contorno del cerchio

ON : Utilizzo di cerchi perimetrati (contorno) attorno ai rettangoli come primo controllo di esclusione

ON + Stack : creazione dei limiti del cerchio in fase di esecuzione all'interno del loop sullo stack

ON + Square Distance : utilizzare le distanze quadrate come ulteriore ottimizzazione per evitare di prendere l'algoritmo di radice quadrata più costoso (Pieter Geerkens).

Ecco un riepilogo delle varie prestazioni di diversi algoritmi, mostrando il tempo necessario per scorrere il ciclo.

inserisci qui la descrizione dell'immagine

L'asse x mostra una maggiore complessità aggiungendo più punti (e quindi rallentando il ciclo). (Ad esempio, a 1000 controlli di punti che appaiono casualmente in uno spazio confidenziale con 20 rettangoli, il ciclo scorre e chiama l'algoritmo 20000 volte.) L'asse y mostra il tempo impiegato (ms) per completare l'intero ciclo usando un'alta risoluzione timer delle prestazioni. Più di 20 ms sarebbe problematico per un gioco decente in quanto non trarrebbe vantaggio dagli alti fps per interpolare un'animazione fluida e il gioco potrebbe apparire così "robusto" a volte.

Risultato 1 : un algoritmo a rilegatura circolare preelaborato con un controllo negativo rapido all'interno del loop migliora le prestazioni del 1900% rispetto all'algoritmo normale (5% del tempo di loop originale senza controllo). Il risultato è approssimativamente proporzionale al numero di iterazioni all'interno di un loop, quindi non importa se controlliamo 10 o 10000 punti che appaiono casualmente. Pertanto, in questa illustrazione è possibile aumentare il numero di oggetti in modo sicuro a 10k senza avvertire una perdita di prestazioni.

Risultato 2 : è stato suggerito da un precedente commento che l'algoritmo potrebbe essere più veloce ma che richiede molta memoria. Tuttavia, si noti che la memorizzazione di un float per la dimensione del cerchio pre-elaborata richiede solo 4 byte. Ciò non dovrebbe costituire un vero problema a meno che l'OP non preveda di eseguire contemporaneamente più di 100000 oggetti. Un approccio alternativo ed efficiente in termini di memoria è quello di calcolare la dimensione massima del cerchio nello stack all'interno del ciclo e lasciarlo fuori dal campo di applicazione con ogni iterazione e quindi praticamente senza utilizzo della memoria per un prezzo sconosciuto della velocità. In effetti, il risultato mostra che questo approccio è effettivamente più lento rispetto all'utilizzo di una dimensione del cerchio pre-elaborata, ma mostra comunque un notevole miglioramento delle prestazioni di circa l'1150% (ovvero l'8% del tempo di elaborazione originale).

Risultato 3 : Migliorare ulteriormente l'algoritmo del risultato 1 utilizzando le distanze al quadrato anziché le distanze effettive e quindi eseguendo un'operazione di radice quadrata computazionalmente costosa. Ciò aumenta solo leggermente le prestazioni (2400%). (Nota: provo anche tabelle hash per array pre-elaborati per approssimazioni di radici quadrate con un risultato simile ma leggermente peggiore)

Risultato 4 : controllo ulteriormente lo spostamento / scontro dei rettangoli; tuttavia, ciò non modifica i risultati di base (come previsto) poiché il controllo logico rimane essenzialmente lo stesso.

Risultato 5 : varco il numero di rettangoli e trovo che l'algoritmo diventa ancora più efficiente tanto meno lo spazio è pieno (non mostrato nella demo). Il risultato è anche un po 'atteso, poiché la probabilità diminuisce per far apparire un punto all'interno di uno spazio minuscolo tra un cerchio e i confini dell'oggetto. Dall'altro estremo, cerco di aumentare di 100 il numero di rettangoli anche all'interno dello stesso piccolo spazio confinato E di variare dinamicamente le loro dimensioni in fase di esecuzione all'interno del ciclo (sin (iteratore)). Ciò funziona ancora molto bene con un aumento delle prestazioni del 570% (o 15% del tempo di loop originale).

Risultato 6 : collaudo algoritmi alternativi suggeriti qui e trovo una differenza molto leggera ma non significativa nelle prestazioni (2%). L'interessante e più semplice algoritmo IsLeft si comporta molto bene con un aumento delle prestazioni del 17% (85% del tempo di calcolo originale) ma da nessuna parte vicino all'efficienza di un algoritmo di controllo negativo rapido.

Il mio punto è innanzitutto considerare la progettazione snella e la logica di gioco, soprattutto quando si tratta di confini ed eventi di collisione. L'attuale algoritmo dei PO è già abbastanza efficiente e un'ulteriore ottimizzazione non è così critica come l'ottimizzazione del concetto stesso stesso. Inoltre, è bene comunicare lo scopo e lo scopo del gioco, poiché l'efficienza di un algoritmo dipende in modo critico da essi.

Suggerisco di provare sempre a confrontare qualsiasi algoritmo complesso durante la fase di progettazione del gioco in quanto il semplice esame del codice semplice potrebbe non rivelare la verità sull'effettiva esecuzione in fase di esecuzione. L'algoritmo suggerito potrebbe non essere nemmeno necessario, se, ad esempio, si desidera semplicemente verificare se il cursore del mouse si trova all'interno di un rettangolo oppure no, o quando la maggior parte degli oggetti è già in contatto. Se la maggior parte dei controlli dei punti si trova all'interno del rettangolo, l'algoritmo sarà meno efficiente. (Tuttavia, sarebbe possibile stabilire un limite di "cerchio interno" come controllo negativo secondario.) I controlli di confine cerchio / sfera sono molto utili per qualsiasi rilevamento decente di collisioni di un gran numero di oggetti che hanno naturalmente uno spazio tra loro .

Rec Points  Iter    OFF     ON     ON_Stack     ON_SqrDist  Ileft Algorithm (Wondra)
            (ms)    (ms)    (ms)    (ms)        (ms)        (ms)
20  10      200     0.29    0.02    0.04        0.02        0.17
20  100     2000    2.23    0.10    0.20        0.09        1.69
20  1000    20000   24.48   1.25    1.99        1.05        16.95
20  10000   200000  243.85  12.54   19.61       10.85       160.58

Sebbene mi piacesse l'approccio insolito e amassi il riferimento di Da Vinci, non penso che avere a che fare con i circoli, per non parlare del raggio, sarebbe così efficace. Inoltre, questa soluzione è ragionevole solo se tutti i rettangoli sono fissi e conosciuti in anticipo
Louis15

Non è necessario correggere la posizione del rettangolo. Usa coordinate relative. Pensa anche a questo. Quel raggio rimane lo stesso, indipendentemente dalla rotazione.
Majte

Questa è un'ottima risposta; meglio ancora perché non ci avevo pensato. Potresti voler notare che è sufficiente usare le distanze quadrate al posto delle distanze effettive, risparmiando la necessità di calcolare sempre una radice quadrata.
Pieter Geerkens,

Algoritmo interessante per test rapidi positivi / negativi! Il problema potrebbe essere una memoria aggiuntiva per salvare i cerchi (e le larghezze) di delimitazione preelaborati, potrebbe essere una buona euristica ma anche notare che ha un uso limitato - principalmente per i casi in cui la memoria non ha molta importanza (rettangoli di dimensioni statiche su oggetti più grandi = oggetti di gioco sprite) e ho tempo per preelaborare.
Wondra,

Modificato + aggiunto test di benchmark.
Majte,

2

La definizione di un rettangolo con 4 punti consente di creare un trapezio. Se tuttavia, lo definissi per x, y, larghezza, altezza e una rotazione attorno al suo centro, potresti semplicemente ruotare il punto che stai controllando con la rotazione inversa del tuo rettangolo (attorno alla stessa origine) e quindi verificare se è nel rettangolo originale.


Hmm, grazie per il suggerimento, ma ruotare e ottenere la rotazione inversa non sembra così efficiente. In effetti sarà difficilmente efficiente come la mia soluzione - per non parlare delle meraviglie
Louis15

Si può notare che la rotazione di un punto 3D con una matrice è di 6 moltiplicazioni e 3 aggiunte e una chiamata di funzione. La soluzione di @ Wondra è nella migliore delle ipotesi equivalente, ma molto meno chiara nelle intenzioni; e più suscettibile agli errori di manutenzione violando DRY
Pieter Geerkens,

@Pieter Geerkens affermazione intersecante, in che modo una delle mie soluzioni viola DRY (e DRY è uno dei principi chiave di programmazione? Non ne hai mai sentito parlare fino ad ora)? E, soprattutto, quali sono gli errori di tali soluzioni? Sempre pronto ad imparare.
Wondra,

@wondra: DRY = Non ripetere te stesso. Lo snippet di codice suggerisce di codificare i dettagli di una matrice per moltiplicazione vettoriale ovunque la funzionalità appaia nel codice invece di invocare un metodo standard da applicazione matrice a vettore.
Pieter Geerkens,

@PieterGeerkens ovviamente suggerisce solo una parte di esso - 1) non hai una matrice esplicita (l'allocazione di una nuova matrice per ogni query comprometterebbe le prestazioni) 2) Uso solo un caso specifico di moltiplicazione, ottimizzato per questo caso facendo cadere il gonfiore del generico uno. È un'operazione di basso livello e dovrebbe rimanere incapsulata per prevenire comportamenti imprevisti.
Wondra,

1

Non ho avuto il tempo di confrontarlo, ma il mio suggerimento sarebbe di memorizzare la matrice di trasformazione che trasforma il rettangolo nel quadrato allineato agli assi nell'intervallo xey da 0 a 1. In altre parole, memorizzare la matrice che trasforma un angolo del rettangolo in (0,0) e quello opposto in (1,1).

Questo sarebbe ovviamente più costoso se il rettangolo viene spostato molto e la collisione viene controllata piuttosto raramente, ma se ci sono molti più controlli rispetto agli aggiornamenti del rettangolo sarebbe almeno più veloce dell'approccio originale del test rispetto a due triangoli, poiché i prodotti a sei punti verrebbero sostituiti con una moltiplicazione di matrici.

Ma come sempre la velocità di questo algoritmo dipende molto dal tipo di controlli che ti aspetti di essere eseguito. Se la maggior parte dei punti non è nemmeno vicina al rettangolo eseguendo un semplice controllo della distanza (ad es. (Point.x - firstCorner.x)> aLargeDistance) potrebbe risultare in una grande accelerazione, mentre potrebbe anche rallentare le cose se quasi tutti i punti sono all'interno del rettangolo.

EDIT: Ecco come sarebbe la mia classe Rectangle:

class Rectangle
{
public:
    Matrix3x3 _transform;

    Rectangle()
    {}

    void setCorners(Vector2 p_a, Vector2 p_b, Vector2 p_c)
    {
        // create a matrix from the two edges of the rectangle
        Vector2 edgeX = p_b - p_a;
        Vector2 edgeY = p_c - p_a;

        // and then create the inverse of that matrix because we want to 
        // transform points from world coordinates into "rectangle coordinates".
        float scaling = 1/(edgeX._x*edgeY._y - edgeY._x*edgeX._y);

        _transform._columns[0]._x = scaling * edgeY._y;
        _transform._columns[0]._y = - scaling * edgeX._y;
        _transform._columns[1]._x = - scaling * edgeY._x;
        _transform._columns[1]._y = scaling * edgeX._x;

        // the third column is the translation, which also has to be transformed into "rectangle space"
        _transform._columns[2]._x = -p_a._x * _transform._columns[0]._x - p_a._y * _transform._columns[1]._x;
        _transform._columns[2]._y = -p_a._x * _transform._columns[0]._y - p_a._y * _transform._columns[1]._y;
    }

    bool isInside(Vector2 p_point)
    {
        Vector2 test = _transform.transform(p_point);
        return  (test._x>=0)
                && (test._x<=1)
                && (test._y>=0)
                && (test._y<=1);
    }
};

Questa è la lista completa del mio benchmark:

#include <cstdlib>
#include <math.h>
#include <iostream>

#include <sys/time.h>

using namespace std;

class Vector2
{
public:
    float _x;
    float _y;

    Vector2()
    :_x(0)
    ,_y(0)
    {}

    Vector2(float p_x, float p_y)
        : _x (p_x)
        , _y (p_y)
        {}

    Vector2 operator-(const Vector2& p_other) const
    {
        return Vector2(_x-p_other._x, _y-p_other._y);
    }

    Vector2 operator+(const Vector2& p_other) const
    {
        return Vector2(_x+p_other._x, _y+p_other._y);
    }

    Vector2 operator*(float p_factor) const
    {
        return Vector2(_x*p_factor, _y*p_factor);
    }

    static float Dot(Vector2 p_a, Vector2 p_b)
    {
        return (p_a._x*p_b._x + p_a._y*p_b._y);
    }
};

bool PointInTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
 // Compute vectors        
 Vector2 v0 = C - A;
 Vector2 v1 = B - A;
 Vector2 v2 = P - A;

 // Compute dot products
 float dot00 = Vector2::Dot(v0, v0);
 float dot01 = Vector2::Dot(v0, v1);
 float dot02 = Vector2::Dot(v0, v2);
 float dot11 = Vector2::Dot(v1, v1);
 float dot12 = Vector2::Dot(v1, v2);

 // Compute barycentric coordinates
 float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
 float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
 float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

 // Check if point is in triangle
 if(u >= 0 && v >= 0 && (u + v) < 1)
    { return true; } else { return false; }
}


bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
 if(PointInTriangle(X,Y,Z,P)) return true;
 if(PointInTriangle(X,Z,W,P)) return true;
 return false;
}

class Matrix3x3
{
public:
    Vector2 _columns[3];

    Vector2 transform(Vector2 p_in)
    {
        return _columns[0] * p_in._x + _columns[1] * p_in._y + _columns[2];
    }
};

class Rectangle
{
public:
    Matrix3x3 _transform;

    Rectangle()
    {}

    void setCorners(Vector2 p_a, Vector2 p_b, Vector2 p_c)
    {
        // create a matrix from the two edges of the rectangle
        Vector2 edgeX = p_b - p_a;
        Vector2 edgeY = p_c - p_a;

        // and then create the inverse of that matrix because we want to 
        // transform points from world coordinates into "rectangle coordinates".
        float scaling = 1/(edgeX._x*edgeY._y - edgeY._x*edgeX._y);

        _transform._columns[0]._x = scaling * edgeY._y;
        _transform._columns[0]._y = - scaling * edgeX._y;
        _transform._columns[1]._x = - scaling * edgeY._x;
        _transform._columns[1]._y = scaling * edgeX._x;

        // the third column is the translation, which also has to be transformed into "rectangle space"
        _transform._columns[2]._x = -p_a._x * _transform._columns[0]._x - p_a._y * _transform._columns[1]._x;
        _transform._columns[2]._y = -p_a._x * _transform._columns[0]._y - p_a._y * _transform._columns[1]._y;
    }

    bool isInside(Vector2 p_point)
    {
        Vector2 test = _transform.transform(p_point);
        return  (test._x>=0)
                && (test._x<=1)
                && (test._y>=0)
                && (test._y<=1);
    }
};

void runTest(float& outA, float& outB)
{
    Rectangle r;
    r.setCorners(Vector2(0,0.5), Vector2(0.5,1), Vector2(0.5,0));

    int numTests = 10000;

    Vector2 points[numTests];

    Vector2 cornerA[numTests];
    Vector2 cornerB[numTests];
    Vector2 cornerC[numTests];
    Vector2 cornerD[numTests];

    bool results[numTests];
    bool resultsB[numTests];

    for (int i=0; i<numTests; ++i)
    {
        points[i]._x = rand() / ((float)RAND_MAX);
        points[i]._y = rand() / ((float)RAND_MAX);

        cornerA[i]._x = rand() / ((float)RAND_MAX);
        cornerA[i]._y = rand() / ((float)RAND_MAX);

        Vector2 edgeA;
        edgeA._x = rand() / ((float)RAND_MAX);
        edgeA._y = rand() / ((float)RAND_MAX);

        Vector2 edgeB;
        edgeB._x = rand() / ((float)RAND_MAX);
        edgeB._y = rand() / ((float)RAND_MAX);

        cornerB[i] = cornerA[i] + edgeA;
        cornerC[i] = cornerA[i] + edgeB;
        cornerD[i] = cornerA[i] + edgeA + edgeB;
    }

    struct timeval start, end;

    gettimeofday(&start, NULL);
    for (int i=0; i<numTests; ++i)
    {
        r.setCorners(cornerA[i], cornerB[i], cornerC[i]);
        results[i] = r.isInside(points[i]);
    }
    gettimeofday(&end, NULL);
    float elapsed = (end.tv_sec - start.tv_sec)*1000;
    elapsed += (end.tv_usec - start.tv_usec)*0.001;
    outA += elapsed;

    gettimeofday(&start, NULL);
    for (int i=0; i<numTests; ++i)
    {
        resultsB[i] = PointInRectangle(cornerA[i], cornerB[i], cornerC[i], cornerD[i], points[i]);
    }
    gettimeofday(&end, NULL);
    elapsed = (end.tv_sec - start.tv_sec)*1000;
    elapsed += (end.tv_usec - start.tv_usec)*0.001;
    outB += elapsed;
}

/*
 * 
 */
int main(int argc, char** argv) 
{
    float a = 0;
    float b = 0;

    for (int i=0; i<5000; i++)
    {
        runTest(a, b);
    }

    std::cout << "Result: " << a << " / " << b << std::endl;

    return 0;
}

Il codice non è certamente bello, ma non vedo immediatamente alcun bug importante. Con quel codice ottengo risultati che indicano che la mia soluzione è circa due volte più veloce se il rettangolo viene spostato tra ogni controllo. Se non si muove, il mio codice sembra essere più di cinque volte più veloce.

Se sai come verrà utilizzato il codice, puoi anche accelerarlo un po 'di più separando la trasformazione e i controlli nelle due dimensioni. Ad esempio, in un gioco di corse sarebbe probabilmente più veloce controllare prima le coordinate che indicano la direzione di guida, poiché molti ostacoli si troveranno davanti o dietro la macchina, ma quasi nessuno sarà a destra o a sinistra.


Interessante, ma non dimenticare che devi anche applicare la rotazione della matrice anche sui punti. Ho un'operazione di putrefazione della matrice nel mio gameengine e posso confrontare il tuo algoritmo in seguito. Per quanto riguarda il tuo ultimo commento. Quindi puoi anche definire un 'cerchio interno' ed effettuare un doppio controllo negativo se il punto si trova al di fuori del cerchio interno e all'interno del cerchio esterno come descritto sopra.
Majte,

Sì, sarebbe d'aiuto se ti aspetti che la maggior parte dei punti si trovi vicino al centro del triangolo. Stavo immaginando una situazione come una pista rettangolare in cui per esempio definisci un percorso rettangolare usando un rettangolo esterno in cui il personaggio deve rimanere all'interno e un rettangolo interno più piccolo da cui deve stare fuori. In quel caso ogni controllo sarebbe vicino al bordo del rettangolo e quei controlli del cerchio probabilmente peggiorerebbero le prestazioni. Certo, questo è un esempio costruito, ma direi che è qualcosa che potrebbe effettivamente accadere.
Lars Kokemohr,

Cose del genere possono succedere, sì. Mi chiedo quale sia il punto debole da confrontare con l'algoritmo. Alla fine si riduce al tuo scopo. Se hai tempo puoi pubblicare il tuo codice, usando il post OP e posso confrontare il tuo algoritmo? Vediamo se il tuo intuito è giusto. Sono curioso dell'esecuzione della tua idea contro l'algoritmo IsLeft.
Majte,
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.