Пересечение линии с прямоугольником AABB?

Желательно без использования какого-либо цикла, так как он будет использоваться в игре.

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

Возможно, я немного погуглил, но так и не разобрался.

Линия определяется с помощью (x1,y1,x2,y2). У прямоугольника тоже есть эти две точки.


person Steffan Donal    schedule 19.09.2010    source источник
comment
Начните с более легкой задачи. Вы знаете, как пересечь бесконечную прямую с другой бесконечной линией?   -  person Eric Lippert    schedule 19.09.2010
comment
Я не думаю, что вы лучше учитесь с помощью сократовского метода; Я понимаю, что не у всех. Скорее, я пытаюсь оценить ваш уровень существующих знаний. Если вы не знаете, как пересечь две линии, вам, вероятно, придется сначала изучить это, прежде чем пытаться пересекать более сложные геометрические фигуры.   -  person Eric Lippert    schedule 20.09.2010
comment
Я в корне не согласен. Иногда лучше просто реализовать чужое решение в коде, убедиться, что оно работает, и забыть о нем, чем изучать теорию решения и реализовывать его самостоятельно. Вы не так многому учитесь, но не все хотят или должны учиться всему.   -  person Minthos    schedule 11.12.2012


Ответы (1)


Я бы порекомендовал просто выполнить проверку пересечения линии-сегмента-линии-сегмента на каждом сегменте линии (ребре), из которого состоит прямоугольник. Вот алгоритм обнаружения пересечения отрезков, который я написал много лет назад, извлеченный из одного из моих старых проектов XNA:

// a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end
static bool Intersects(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 intersection)
{
    intersection = Vector2.Zero;

    Vector2 b = a2 - a1;
    Vector2 d = b2 - b1;
    float bDotDPerp = b.X * d.Y - b.Y * d.X;

    // if b dot d == 0, it means the lines are parallel so have infinite intersection points
    if (bDotDPerp == 0)
        return false;

    Vector2 c = b1 - a1;
    float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
    if (t < 0 || t > 1)
        return false;

    float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
    if (u < 0 || u > 1)
        return false;

    intersection = a1 + t * b;

    return true;
}

Я оставлю ввод каждого ребра в описанный выше метод и сбор результатов в качестве упражнения для читателя :)


Изменить 1 год спустя, теперь я поступил в университет и прошел курс графики:

Взгляните на алгоритм Коэна-Сазерленда, чтобы сделать это эффективно, когда вы иметь большой набор линий, большинство из которых не пересекают прямоугольник. Он использует сетку из 9 сегментов, и вы помещаете каждую конечную точку линии в область указанной сетки:

сетка

Используя это, мы можем сказать, не будет ли пересечений линий:

сетка с линиями

Например, здесь CD не будет пересекать прямоугольник (показан красным на первом изображении), так как C и D находятся в верхнем ряду, а AB тоже. Для тех, где линия может пересекать прямоугольник, мы должны попробовать пересечения линии.

То, как секции пронумерованы/помечены, позволяет нам просто выполнить x AND y != 0 (где x и y — метки секций для каждой из конечных точек линии), чтобы определить, не будет ли пересечения.

Использование этого метода означает, что у нас будет намного меньше пересечений линий, что значительно ускоряет все это.

person Callum Rogers    schedule 19.09.2010
comment
Это будет достаточно легко сделать. Большое Вам спасибо. - person Steffan Donal; 19.09.2010
comment
В приведенном выше примере кода это на самом деле перекрестные, а не скалярные произведения. - person Nathanael Weiss; 16.08.2012
comment
Алгоритм Коэна-Сазерленда, похоже, заключается в отсечении и поиске пересечения, а не в том, есть ли какое-либо пересечение - так что, конечно, это будет медленнее? - person paulm; 21.01.2014
comment
Хм, алгоритм Коэна-Сазерленда предполагает, что конечное пространство разделено на 9 равных областей (или 27 в 3d). Что происходит, когда ваше пространство бесконечно? - person Matt Esch; 30.01.2014
comment
@MattEsch: я думаю, вы просто сделаете внешние 8 (26) областей бесконечными. - person Moberg; 17.02.2014
comment
Что, если линии бесконечны? - person rxantos; 03.04.2014
comment
@rxantos: Тогда вы имеете дело с полупространствами и пересечениями полупространств. - person Deco; 30.09.2014
comment
Если подумать, прямоугольник — это everything subtract [intersection of four halfspaces]. Если вы удалите одно из этих полупространств (чтобы получить бесконечные линии), останется ли оно прямоугольником? - person Deco; 30.09.2014
comment
(... Я только что понял, что вы, вероятно, имеете в виду, что ЛИНИИ бесконечны, а не линии прямоугольника. В этом случае, безусловно, есть способ выяснить, какую из внешних областей пересечет линия.) - person Deco; 30.09.2014