Bagaimana cara saya memeriksa secara efisien apakah suatu titik berada di dalam kotak yang diputar?

11

Sebagian demi optimasi, sebagian untuk tujuan pembelajaran, saya akan berani bertanya: Bagaimana saya bisa paling efisien memeriksa apakah titik 2D Pada di dalam persegi panjang yang diputar 2D XYZW, menggunakan C # atau C ++?

Saat ini, apa yang saya lakukan adalah menggunakan algoritma "point in triangle" yang ditemukan dalam buku Real Time Collision Detection , dan menjalankannya dua kali (untuk dua segitiga yang membentuk persegi panjang, katakanlah XYZ dan 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;
}

Namun, saya merasa mungkin ada cara yang lebih bersih dan lebih cepat. Secara khusus, untuk mengurangi jumlah operasi matematika.

Louis15
sumber
Apakah Anda memiliki banyak poin, atau apakah Anda memiliki banyak persegi panjang? Itu pertanyaan pertama yang harus Anda tanyakan pada diri sendiri sebelum Anda mencoba mengoptimalkan tugas sekecil itu.
sam hocevar
Poin yang bagus. Saya akan memiliki jumlah poin yang sangat tinggi, tetapi lebih dari empat persegi panjang untuk diperiksa.
Louis15
Pertanyaan terkait tentang menemukan jarak titik ke persegi panjang yang diputar . Ini adalah kasus yang merosot dari itu (memeriksa hanya ketika jaraknya 0). Tentu saja, akan ada optimisasi yang berlaku di sini yang tidak ada.
Anko
Sudahkah Anda mempertimbangkan untuk memutar titik ke dalam kerangka referensi persegi panjang?
Richard Tingle
@ RichardTingle Sebenarnya saya tidak melakukannya di awal. Kemudian saya melakukannya, karena saya pikir itu berkaitan dengan salah satu jawaban yang diberikan di bawah ini. Tetapi hanya untuk memperjelas: dalam apa yang Anda sarankan, setelah memutar titik ke kerangka referensi persegi panjang, maka orang harus memeriksa inklusi hanya dengan perbandingan logis antara max.x, min.x, dll?
Louis15

Jawaban:

2

Optimalisasi yang mudah dan langsung akan mengubah kondisi akhir di 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; }
  }
}

kode sudah cukup banyak PointInRectangle, syaratnya (u + v) < 1ada di sana untuk memeriksa jika tidak dalam "kedua" segitiga persegi panjang.

Sebagai alternatif, Anda juga dapat melakukan isLeft(contoh kode pertama pada halaman, juga sangat menjelaskan) tes empat kali, dan memeriksa bahwa mereka semua mengembalikan hasil dengan tanda yang sama (yang tergantung pada apakah poin diberikan dalam searah jarum jam atau berlawanan arah jarum jam) untuk intinya berada di dalam. Ini juga berfungsi untuk poligon cembung lainnya.

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);
}
keajaiban
sumber
Hebat. Saya tidak tahu apakah saya suka lebih banyak saran Anda, yang benar-benar lebih cepat dan jauh lebih elegan daripada saya, atau jika saya lebih suka Anda perhatikan bahwa kode PointInTri saya dapat dengan mudah menjadi PointInRec! Terima kasih
Louis15
+1 untuk isLeftmetode ini. Tidak memerlukan fungsi trigonometri (seperti Vector2.Dothalnya), yang mempercepat banyak hal.
Anko
Btw, tidak bisakah kode di-tweak (tidak menguji; tidak memiliki cara di komputer ini), dengan memasukkan isLeft langsung dalam fungsi utama, dan dengan mengganti operator "&&" dengan "||" melalui logika terbalik? 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 Saya rasa Anda tidak perlu - keduanya && dan || akan berhenti mengeksekusi pernyataan lebih lanjut jika satu negatif / positif ditemukan (atau adakah alasan lain?). Mendeklarasikan isLeftsebagai inline kompiler akan melakukan sesuatu yang serupa untuk Anda (dan mungkin lebih baik dari yang Anda bisa, karena para insinyur yang menulis kompiler mengetahui CPU terbaik, memilih opsi apa pun yang tercepat) membuat kode Anda lebih mudah dibaca dengan efek yang sama atau lebih baik.
wonderra
8

Sunting: Komentar OP telah skeptis tentang efisiensi cek terikat lingkaran negatif yang disarankan untuk meningkatkan algoritma agar dapat memeriksa apakah titik 2D sewenang-wenang terletak di dalam persegi panjang yang diputar dan / atau bergerak. Mengotak-atik mesin game 2D saya (OpenGL / C ++), saya melengkapi jawaban saya dengan memberikan tolok ukur kinerja algoritma saya terhadap algoritma point-in-rectangle-check OP saat ini (dan variasi).

Saya awalnya menyarankan untuk meninggalkan algoritma di tempat (karena hampir optimal), tetapi menyederhanakan melalui logika permainan belaka: (1) menggunakan lingkaran pra-diproses di sekitar persegi panjang asli; (2) melakukan pengecekan jarak dan jika titik terletak di dalam lingkaran yang diberikan; (3) gunakan OP atau algoritma langsung lainnya (saya sarankan algoritma isLeft seperti yang disediakan dalam jawaban lain). Logika di belakang saran saya adalah bahwa memeriksa apakah suatu titik di dalam lingkaran jauh lebih efisien daripada pemeriksaan batas dari persegi panjang yang diputar atau poligon lain.

Skenario awal saya untuk uji benchmark adalah menjalankan sejumlah besar titik yang muncul dan menghilang (yang posisinya berubah di setiap loop permainan) di ruang terbatas yang akan diisi dengan sekitar 20 kotak yang berputar / bergerak. Saya telah menerbitkan video ( tautan youtube ) untuk tujuan ilustrasi. Perhatikan parameternya: jumlah titik, angka, atau persegi yang muncul secara acak. Saya akan melakukan benchmark dengan parameter berikut:

OFF : Algoritma langsung seperti yang disediakan oleh OP tanpa pemeriksaan negatif batas lingkaran

ON : Menggunakan lingkaran per-diproses (batas) di sekitar persegi panjang sebagai pemeriksaan pengecualian pertama

ON + Stack : Membuat batas lingkaran saat run-time di dalam loop pada stack

ON + Kuadrat Jarak : Menggunakan jarak kuadrat sebagai optimasi lebih lanjut untuk menghindari pengambilan algoritma kuadrat yang lebih mahal (Pieter Geerkens).

Berikut ini adalah ringkasan dari berbagai kinerja algoritma yang berbeda dengan menunjukkan waktu yang diperlukan untuk beralih melalui loop.

masukkan deskripsi gambar di sini

Sumbu x menunjukkan peningkatan kompleksitas dengan menambahkan lebih banyak titik (dan dengan demikian memperlambat loop). (Misalnya, pada 1000 titik yang muncul secara acak memeriksa dalam ruang tertutup dengan 20 persegi panjang, loop berulang dan memanggil algoritme 20000 kali.) Sumbu-y menunjukkan waktu yang diperlukan (ms) untuk menyelesaikan seluruh loop menggunakan resolusi tinggi timer kinerja. Lebih dari 20 ms akan bermasalah untuk game yang layak karena tidak akan mengambil keuntungan dari fps tinggi untuk menginterpolasi animasi yang halus dan permainan mungkin muncul 'kasar' di kali.

Hasil 1 : Algoritma terikat terikat pra-diproses dengan cek negatif cepat dalam loop meningkatkan kinerja sebesar 1900% dibandingkan dengan algoritma biasa (5% dari waktu loop asli tanpa cek). Hasilnya memegang kira-kira sebanding dengan jumlah iterasi dalam satu loop, sehingga tidak masalah jika kita memeriksa 10 atau 10000 poin yang muncul secara acak. Dengan demikian, dalam ilustrasi ini orang dapat meningkatkan jumlah objek dengan aman hingga 10rb tanpa merasakan kehilangan kinerja.

Hasil 2 : Telah dikomentari oleh komentar sebelumnya bahwa algoritma mungkin lebih cepat tetapi intensif memori. Namun, perhatikan bahwa menyimpan pelampung untuk ukuran lingkaran pra-proses hanya membutuhkan 4 byte. Ini seharusnya tidak menimbulkan masalah nyata kecuali OP berencana untuk menjalankan 100000 objek secara bersamaan. Alternatif dan pendekatan memori efisien adalah untuk menghitung ukuran lingkaran maksimum pada tumpukan dalam loop dan membiarkannya keluar dari ruang lingkup dengan setiap iterasi dan dengan demikian praktis tidak menggunakan memori untuk beberapa harga kecepatan yang tidak diketahui. Memang, hasilnya menunjukkan bahwa pendekatan ini memang lebih lambat daripada menggunakan ukuran lingkaran pra-diproses, tetapi masih menunjukkan peningkatan kinerja yang cukup besar sekitar 1150% (yaitu 8% dari waktu pemrosesan asli).

Hasil 3 : Saya lebih meningkatkan algoritma hasil 1 dengan menggunakan jarak kuadrat bukan jarak yang sebenarnya dan dengan demikian mengambil operasi root kuadrat komputasi mahal. Ini hanya sedikit meningkatkan kinerja (2400%). (Catatan: Saya juga mencoba tabel hash untuk array pra-diproses untuk perkiraan akar kuadrat dengan hasil yang serupa tetapi sedikit lebih buruk)

Hasil 4 : Saya selanjutnya memeriksa bergerak / bertabrakan persegi panjang di sekitar; Namun, ini tidak mengubah hasil dasar (seperti yang diharapkan) karena pemeriksaan logis pada dasarnya tetap sama.

Hasil 5 : Saya memvariasikan jumlah persegi panjang dan menemukan bahwa algoritma menjadi lebih efisien semakin sedikit ruang crowdy diisi (tidak ditampilkan dalam demo). Hasilnya juga agak diharapkan, karena probabilitas berkurang untuk titik muncul dalam ruang kecil antara lingkaran dan batas-batas objek. Pada ekstrem yang lain, saya mencoba untuk menambah jumlah persegi panjang juga 100 dalam ruang kecil yang sama DAN memvariasikannya secara dinamis dalam ukuran saat run time di dalam loop (sin (iterator)). Ini masih berkinerja sangat baik dengan peningkatan kinerja sebesar 570% (atau 15% dari waktu loop asli).

Hasil 6 : Saya menguji algoritma alternatif yang disarankan di sini dan menemukan perbedaan yang sangat kecil tetapi tidak signifikan dalam kinerja (2%). Algoritme IsLeft yang menarik dan lebih sederhana memiliki kinerja yang sangat baik dengan peningkatan kinerja sebesar 17% (85% dari waktu perhitungan awal), tetapi tidak ada efisiensi algoritma pengecekan negatif yang cepat.

Maksud saya adalah pertama-tama mempertimbangkan desain ramping dan logika game, terutama ketika berhadapan dengan batas dan peristiwa tabrakan. Algoritma OPs saat ini sudah cukup efisien dan optimasi lebih lanjut tidak sepenting mengoptimalkan konsep yang mendasarinya sendiri. Selain itu, baik untuk mengomunikasikan ruang lingkup dan tujuan permainan, karena efisiensi suatu algoritma sangat tergantung pada mereka.

Saya menyarankan untuk selalu berusaha melakukan tolok ukur algoritme yang kompleks selama tahap desain game karena hanya dengan melihat kode biasa mungkin tidak mengungkapkan kebenaran tentang kinerja run-time aktual. Algoritme yang disarankan mungkin tidak diperlukan di sini, jika, misalnya, seseorang hanya ingin menguji apakah kursor mouse berada di dalam persegi panjang atau tidak, atau, ketika sebagian besar objek sudah menyentuh. Jika sebagian besar titik memeriksa berada dalam persegi panjang, algoritme akan kurang efisien. (Namun, maka akan mungkin untuk menetapkan batas 'lingkaran dalam' sebagai pemeriksaan negatif sekunder.) Pemeriksaan batas lingkaran / bola sangat berguna untuk setiap deteksi tabrakan yang layak dari sejumlah besar objek yang secara alami memiliki ruang di antaranya. .

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
Majte
sumber
Meskipun saya menyukai pendekatan yang tidak biasa dan menyukai referensi Da Vinci, saya tidak berpikir berurusan dengan lingkaran, apalagi radius, akan seefisien itu. Juga, solusi itu hanya masuk akal jika semua persegi panjang diperbaiki dan diketahui sebelumnya
Louis15
Posisi persegi panjang tidak perlu diperbaiki. Gunakan koordinat relatif. Pikirkan juga seperti ini. Jari-jari itu tetap sama, tidak peduli rotasi.
Majte
Ini jawaban yang bagus; lebih baik lagi karena saya belum memikirkannya. Anda mungkin ingin mencatat bahwa cukup menggunakan jarak kuadrat sebagai pengganti jarak yang sebenarnya, sehingga tidak perlu lagi menghitung akar kuadrat.
Pieter Geerkens
Algoritma menarik untuk pengujian positif / negatif yang cepat! Masalahnya mungkin memori tambahan untuk menyimpan lingkaran pembatas preprocessed (dan lebar), mungkin heuristik yang baik tetapi juga mencatat itu memiliki penggunaan terbatas - kebanyakan untuk kasus-kasus di mana memori tidak terlalu penting (persegi ukuran statis pada objek yang lebih besar = objek permainan sprite) dan punya waktu untuk preprocess.
Wonderra
Diedit + ditambahkan tes benchmark.
Majte
2

Mendefinisikan sebuah persegi panjang dengan 4 poin memungkinkan untuk membuat trapesium. Namun, jika Anda akan mendefinisikannya dengan x, y, lebar, tinggi dan rotasi di tengahnya, Anda bisa memutar titik yang Anda periksa dengan rotasi terbalik persegi panjang Anda (sekitar asal yang sama) dan kemudian periksa apakah itu dalam persegi panjang asli.

Peethor
sumber
Hmm, terima kasih atas sarannya, tetapi memutar dan mendapatkan rotasi terbalik sepertinya tidak efisien. Bahkan itu akan hampir tidak seefisien solusi saya - belum lagi keajaiban
Louis15
Anda dapat mencatat bahwa memutar titik 3D dengan matriks adalah 6 perkalian dan 3 tambahan, dan satu panggilan fungsi. Solusi @ wondra paling tidak setara, tetapi tidak begitu jelas maksudnya; dan lebih rentan terhadap kesalahan pemeliharaan melalui pelanggaran KERING
Pieter Geerkens
@Pieter Geerkens mengklaim klaim, bagaimana salah satu solusi saya melanggar KERING (dan apakah KERING salah satu prinsip pemrograman utama? Belum pernah mendengarnya sampai sekarang)? Dan yang paling penting, kesalahan apa yang dimiliki solusi tersebut? Selalu siap belajar.
Wonderra
@wondra: KERING = Jangan Ulangi Diri Anda. Cuplikan kode Anda menyarankan untuk mengkodekan rincian matriks dengan perkalian vektor di mana saja yang fungsionalitasnya muncul dalam kode alih-alih menggunakan metode matriks-aplikasi-ke-Vector standar.
Pieter Geerkens
@PieterGeerkens tentu saja hanya menyarankan sebagian saja - 1) Anda tidak memiliki matriks secara eksplisit (mengalokasikan matriks baru untuk setiap permintaan akan menekan kinerja dengan keras) 2) Saya hanya menggunakan kasus perkalian khusus, dioptimalkan untuk kasus ini menjatuhkan lapisan generik satu. Ini adalah operasi tingkat rendah dan harus tetap dienkapsulasi untuk mencegah perilaku yang tidak terduga.
wonderra
1

Saya tidak punya waktu untuk membandingkan ini, tetapi saran saya adalah untuk menyimpan matriks transformasi yang mengubah persegi panjang menjadi persegi yang sejajar dalam rentang x dan y dari 0 hingga 1. Dengan kata lain menyimpan matriks yang mengubah satu sudut persegi panjang menjadi (0,0) dan sebaliknya menjadi (1,1).

Ini tentu saja akan lebih mahal jika persegi panjang itu banyak dipindahkan dan tabrakan diperiksa agak jarang, tetapi jika ada lebih banyak pemeriksaan daripada pembaruan ke persegi panjang itu setidaknya akan lebih cepat daripada pendekatan asli pengujian terhadap dua segitiga, karena produk enam titik akan diganti dengan satu perkalian matriks.

Tapi seperti biasa kecepatan algoritma ini sangat tergantung pada jenis pemeriksaan yang Anda harapkan dilakukan. Jika sebagian besar titik bahkan tidak dekat dengan persegi panjang yang melakukan pemeriksaan jarak sederhana (mis. (Point.x - firstCorner.x)> aLargeDistance) dapat menghasilkan percepatan yang besar, sementara itu mungkin bahkan memperlambat segalanya jika hampir semua titik berada di dalam persegi panjang.

EDIT: Beginilah tampilan kelas Rectangle saya:

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);
    }
};

Ini adalah daftar lengkap tolok ukur saya:

#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;
}

Kode itu tentu saja tidak indah, tetapi saya tidak segera melihat bug utama. Dengan kode itu saya mendapatkan hasil yang menunjukkan bahwa solusi saya sekitar dua kali lebih cepat jika persegi panjang dipindahkan di antara setiap pemeriksaan. Jika tidak bergerak maka kode saya sepertinya lebih dari lima kali lebih cepat.

Jika Anda tahu bagaimana kode akan digunakan, Anda bahkan dapat mempercepatnya sedikit lebih dengan memisahkan transformasi dan pemeriksaan menjadi dua dimensi. Misalnya dalam permainan balap mungkin akan lebih cepat untuk memeriksa koordinat terlebih dahulu yang menunjuk ke arah mengemudi, karena banyak rintangan akan ada di depan atau di belakang mobil, tetapi hampir tidak ada yang akan kanan atau kiri itu.

Lars Kokemohr
sumber
Menarik, tetapi jangan lupa Anda juga perlu menerapkan rotasi matriks pada titik-titik juga. Saya memiliki operasi pembusukan matriks di mesin permainan saya dan dapat membuat tolok ukur algoritme Anda nanti. Berkenaan dengan komentar terakhir Anda. Kemudian Anda dapat menentukan 'lingkaran dalam' juga dan melakukan pemeriksaan negatif ganda jika titik tersebut berada di luar lingkaran dalam dan di luar lingkaran seperti dijelaskan di atas.
Majte
Ya, itu akan membantu jika Anda mengharapkan sebagian besar poin berada di dekat tengah segitiga. Saya membayangkan situasi seperti trek balap persegi panjang di mana Anda misalnya mendefinisikan jalur persegi panjang dengan menggunakan persegi panjang luar yang karakternya harus tetap berada di dalam dan persegi panjang dalam yang lebih kecil yang harus dihindari. Dalam hal ini, setiap cek akan dekat dengan batas persegi panjang dan pemeriksaan lingkaran itu mungkin hanya akan membuat kinerja menjadi lebih buruk. Memang, itu adalah contoh yang dibangun, tetapi saya akan mengatakan itu adalah sesuatu yang benar-benar bisa terjadi.
Lars Kokemohr
Hal-hal seperti itu bisa terjadi, ya. Saya bertanya-tanya apa sweet spot untuk berbalik melawan algoritma. Pada akhirnya bermuara pada tujuan Anda. Jika Anda punya waktu, bisakah Anda memposting kode Anda, menggunakan pos OP dan saya dapat membuat tolok ukur algoritme Anda? Mari kita lihat apakah intuisi Anda benar. Saya ingin tahu tentang kinerja ide Anda terhadap Algoritma IsLeft.
Majte