Menemukan ubin mana yang berpotongan dengan garis, tanpa melewati semuanya atau melewatkannya

10

Saya sudah menatap masalah ini selama beberapa hari sekarang. Saya memasang grafik ini untuk membantu saya memvisualisasikan masalah: masukkan deskripsi gambar di sini (dari grafik, kita tahu bahwa garis berpotongan [1, 1], [1, 2], [2, 2], [2, 3], berakhir dengan [ 3,3])

Saya ingin melangkah sepanjang garis ke setiap ruang kotak dan memeriksa untuk melihat apakah bahan ruang kotak solid. Saya merasa sudah tahu matematika yang terlibat, tetapi saya belum bisa merangkai semuanya. Saya menggunakan ini untuk menguji garis pandang dan menghilangkan node setelah jalan ditemukan melalui algoritma pathfinding saya - agen saya tidak dapat melihat melalui blok padat, oleh karena itu mereka tidak dapat bergerak melalui satu, oleh karena itu simpul tidak dihilangkan dari jalan karena itu diperlukan untuk menavigasi sudut.

Jadi, saya membutuhkan algoritma yang akan melangkah sepanjang garis ke setiap ruang kotak yang bersinggungan. Ada ide?

Saya telah melihat banyak algoritma umum, seperti Bresenham, dan algoritma yang melangkah pada interval yang telah ditentukan sebelumnya (sayangnya, metode ini melewatkan ubin jika mereka berpotongan dengan irisan yang lebih kecil daripada ukuran langkah).

Saya mengisi papan tulis saya sekarang dengan massa fungsi floor () dan ceil () - tapi itu menjadi terlalu rumit dan saya khawatir itu bisa menyebabkan perlambatan.

Busa
sumber
Anda sudah tahu cara menguji persimpangan garis-kotak yang sebenarnya, bukan? Hanya bertanya, karena ini relevan dengan jawabannya.
TravisG
kemungkinan duplikat dari Bagaimana cara menggeneralisasi algoritma garis Bresenham ke titik akhir floating-point? (pertanyaannya sebenarnya bukan tentang Bresenham)
sam hocevar

Jawaban:

6

Jika Anda tahu blok awal (Anda tahu titik X dan Anda tidak memasukkan blok [0,1] dalam daftar blok, jadi saya kira Anda juga tahu blok awal), saya pikir Anda harus menggunakan algoritma Bresenham. Anda menulis, Anda melihatnya.

Ini algoritma yang cocok untuk masalah ini. Dapat juga ditulis dengan cara, hanya menghitung dengan bilangan bulat. Anda dapat menemukan banyak implementasi di web.

EDIT:

Maaf, saya belum menyadari bahwa Bresenham tidak akan menemukan semua blok. Jadi saya menemukan solusi yang lebih baik . Ada juga kode yang ditulis dalam C ++, tapi saya pikir seharusnya tidak sulit untuk dimengerti :)

zacharmarz
sumber
1
Alasan saya melihat melewati algoritma Bresenham murni karena gambar di Wikipedia. ( en.wikipedia.org/wiki/File:Bresenham.svg ) Anda dapat melihat bahwa garis tersebut mencegat beberapa kotak yang tidak diarsir, meskipun hampir tidak. Saya membutuhkan sesuatu yang akan mendeteksi setiap ubin, terlepas dari seberapa kecil irisannya. Sunting: Tampaknya saya salah mengerti bresenham. Saya perlu membalikkannya - saya memiliki poin pertama dan terakhir, dan saya membutuhkan ubin yang bersinggungan - daripada garis yang akan lebih baik untuk plot.
Suds
@JustSuds: Periksa pembaruan di pos.
zacharmarz
Hai hei! yang hampir secara langsung cocok dengan apa yang saya miliki di papan tulis saya! Terima kasih, sistem saya sekarang diimplementasikan dan berfungsi. :-)
Suds
Bisakah Anda menghapus bagian tentang algoritma Bresenham karena tidak menjawab pertanyaan? Jangan khawatir, itu akan tetap dalam riwayat edit jawaban Anda.
zenith
1

Kode pada contoh yang digunakan tautan jawaban yang diterima memerlukan beberapa penyesuaian untuk garis diagonal sempurna. Berikut ini adalah aplikasi demo lengkap yang ditulis dengan Qt (C ++ dan QML).

persimpangan garis grid

Kode C ++ yang relevan:

void rayCast()
{
    if (!isComponentComplete())
        return;

    mTiles.clear();
    mTiles.fill(QColor::fromRgb(255, 222, 173), mSizeInTiles.width() * mSizeInTiles.height());

    const QPoint startTile = startTilePos();
    const QPoint endTile = endTilePos();
    // http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
    int x0 = startTile.x();
    int y0 = startTile.y();
    int x1 = endTile.x();
    int y1 = endTile.y();

    int dx = abs(x1 - x0);
    int dy = abs(y1 - y0);
    int x = x0;
    int y = y0;
    int n = 1 + dx + dy;
    int x_inc = (x1 > x0) ? 1 : -1;
    int y_inc = (y1 > y0) ? 1 : -1;
    int error = dx - dy;
    dx *= 2;
    dy *= 2;

    for (; n > 0; --n)
    {
        visit(x, y);

        if (error > 0)
        {
            x += x_inc;
            error -= dy;
        }
        else if (error < 0)
        {
            y += y_inc;
            error += dx;
        }
        else if (error == 0) {
            // Ensure that perfectly diagonal lines don't take up more tiles than necessary.
            // http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html?showComment=1281448902099#c3785285092830049685
            x += x_inc;
            y += y_inc;
            error -= dy;
            error += dx;
            --n;
        }
    }

    update();
}
Mitch
sumber