Нахождение, какие плитки пересекаются линией, без циклического прохождения всех их или пропуская любые

10

Я смотрю на эту проблему уже несколько дней. Я установил этот график, чтобы помочь мне визуализировать проблему: введите описание изображения здесь (из графика мы знаем, что линия пересекает [1, 1], [1, 2], [2, 2], [2, 3], заканчиваясь на [ 3,3])

Я хочу пройти по линии к каждому пространству сетки и проверить, является ли материал пространства сетки твердым. Я чувствую, что уже знаю, как работает математика, но я пока не смог ее связать. Я использую это для проверки прямой видимости и устранения узлов после того, как путь найден с помощью моих алгоритмов поиска пути - мои агенты не могут видеть сквозь сплошной блок, поэтому они не могут перемещаться через один, поэтому узел не исключается из пути, потому что он требуется для навигации по углу.

Итак, мне нужен алгоритм, который будет двигаться вдоль линии к каждому пространству сетки, которое он пересекает. Любые идеи?

Я рассмотрел множество распространенных алгоритмов, таких как алгоритм Брезенхэма, и алгоритм, который шагает с заранее заданными интервалами вдоль линии (к сожалению, этот метод пропускает плитки, если они пересекаются с меньшим клином, чем размер шага).

Сейчас я заполняю свою доску массой функций floor () и ceil (), но она становится слишком сложной, и я боюсь, что это может вызвать замедление.

мыльная пена
источник
Вы уже знаете, как проверить фактическое пересечение линейных блоков, верно? Просто спрашиваю, потому что это имеет отношение к ответу.
TravisG
Возможный дубликат Как я могу обобщить линейный алгоритм Брезенхэма для конечных точек с плавающей точкой? (вопрос на самом деле не о Брезенхэме)
Сэм Хоцевар

Ответы:

6

Если вы знаете начальный блок (вы знаете точку X и вы не включили блок [0,1] в список блоков, поэтому, я полагаю, вы знаете также начальный блок), я думаю, что вы обязательно должны использовать алгоритм Брезенхэма. Вы написали, вы смотрели на это.

Это подходящий алгоритм для этой проблемы. Он также может быть записан таким образом, что он вычисляется только с целыми числами. Вы можете найти множество реализаций в сети.

РЕДАКТИРОВАТЬ:

Извините, я не понял, что Брезенхем не найдет все блоки. Поэтому я нашел лучшее решение . Есть также код, написанный на C ++, но я думаю, что это не должно быть трудно понять :)

zacharmarz
источник
1
Причина, по которой я посмотрел мимо алгоритма Брезенхема, была исключительно из-за изображения в Википедии. ( en.wikipedia.org/wiki/File:Bresenham.svg ) Вы можете видеть, что линия пересекает некоторые незатененные квадраты, хотя и едва. Мне нужно что-то, что будет определять каждую плитку, независимо от того, насколько бесконечно маленький срез. Изменить: Похоже, что я все равно неправильно понял Bresenham. Мне нужно повернуть вспять - у меня есть первая и последняя точка, и мне нужны плитки, которые она пересекает, - а не линия, которую лучше всего построить.
Суд
@JustSuds: проверьте наличие обновлений в сообщении.
zacharmarz
Эй эй! это почти соответствует тому, что у меня на доске! Спасибо, моя система сейчас внедрена и работает. :-)
Suds
Вы можете удалить часть об алгоритме Брезенхэма, поскольку он не отвечает на вопрос? Не волнуйтесь, он останется в истории редактирования вашего ответа.
Зенит
1

Код в примере, на который ссылается принятый ответ, нуждается в некоторой настройке для идеально диагональных линий. Вот полное демонстрационное приложение, написанное на Qt (C ++ и QML).

пересечение линии сетки

Соответствующий код C ++:

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();
}
Митч
источник