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

У меня есть список прямоугольников, и мне нужно создать список пересекающихся прямоугольников.
Прямоугольники определяются с помощью

  • Точка
  • Размер
  • Boolean, может ли прямоугольник двигаться
  • Boolean, может ли прямоугольник быть удален

Никакие прямоугольники не могут быть перемещены, но не могут быть удалены

Пересечение определяется с помощью

  • Указатель на первый прямоугольник
  • Указатель на второй прямоугольник
  • Список точек первого прямоугольника, находящихся во втором
  • Список точек второго прямоугольника, находящихся в первом

Для этого мне нужен контейнер, так как прямоугольники можно добавлять, удалять или перемещать. операции, которые мне нужны:

  1. Вставка прямоугольника
  2. Удаление прямоугольника (возможно только для тех, кто отмечен для этого)
  3. Изменение положения прямоугольника (не размера, возможно только с отмеченными для него)
  4. Генерация множества пересечений.

Как мне реализовать такой контейнер? Я могу легко сделать это, используя метод перекрестной проверки, но это будет далеко не оптимизировано.
Я подумал о том, чтобы сохранить карту прямоугольника -> пересечения, а затем всякий раз, когда прямоугольник добавляется, проверять, пересекает ли он что-либо, и добавлять пересечение на карту. , и когда он будет удален, удалите ключ с карты, но я не знаю, как проверить, пересекает ли он что-либо быстро, или как перемещать прямоугольники без удаления и повторной вставки.
Я могу использовать C++11.


person Dani    schedule 16.12.2011    source источник


Ответы (2)


Предполагая систему координат слева направо / сверху вниз, пересечением двух прямоугольников является прямоугольник, вершина которого является самой нижней из вершин, нижняя часть является самой верхней из низов, левая, если самая правая из левых и правая является самым левым из правых.

Вы можете эффективно запускать тесты, если можете косвенно обращаться к прямоугольникам по контейнерам, отсортированным по левому, верхнему, правому и нижнему краю.

Альтернативой может быть использование ключа (для карты), представляющего собой пару x/delta с оператором‹, который учитывает a<b везде, где a.x+a.delta < b.x, и то же самое для y. Необработанная точка — это просто прямоугольник размера 1.

По сути, вам нужен контейнер для самого прямоугольника (не должен перераспределять прямоугольники при изменении, поэтому может работать std::list) и два std::maps (для сопоставления horz и vert), имеющие пары место/размер в качестве ключа и итератор списка (может быть прямоугольным указателем, полученным из &*iter) в качестве значения.

person Emilio Garavaglia    schedule 16.12.2011

Моя рекомендация была бы контейнером, смутно похожим на:

class region {
    typedef std::vector<vector*> subregion;
    std::array<std::array<subregion, 100>, 100> locations;
    std::vector<rectangle> children;
public:
    std::vector<rectangle>::iterator add(rectangle addme);
    void remove(std::vector<rectangle>::iterator deleteme);
    void move(std::vector<rectangle>::iterator moveme, point to);
    void intersect(std::vector<rectangle>::iterator left, 
                   std::vector<rectangle>::iterator right);
};

Член children — это просто повторяющийся список прямоугольников в контейнере. Каждая подобласть составляет одну сотую от общей области, которая может содержать прямоугольники. При добавлении прямоугольника указатели на него добавляются ко всем подобластям, которых касается прямоугольник. Когда прямоугольник удаляется, его указатели удаляются из всех подобластей, которых касается прямоугольник. Ход фактически представляет собой удаление, обновление прямоугольника, а затем добавление.

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

person Mooing Duck    schedule 16.12.2011
comment
Это имеет ту же сложность, что и перекрестная проверка - person Dani; 16.12.2011
comment
Технически да, это так. Однако постоянная во фронте в тысячу раз меньше. В зависимости от ваших данных может случиться так, что большинство случаев станут O (1), поскольку они достаточно далеко от других прямоугольников. - person Mooing Duck; 16.12.2011
comment
константа не может быть в тысячу раз меньше. в той же теории вы можете разделить на бесконечность и сделать это O(1) для произвольного случая. - person Dani; 16.12.2011
comment
@Dani: да, ты мог бы. Это также потребует бесконечного количества оперативной памяти. Твой выбор. В моем коде используется массив 100x100, но если вам нужно больше/меньше скорости, вы можете изменить это число. Просто обратите внимание на компромисс между скоростью и памятью. - person Mooing Duck; 16.12.2011
comment
бесконечное количество оперативной памяти требует бесконечного времени для выделения, поэтому даже если у вас была бесконечная оперативная память, она все равно не будет O(1) - person Dani; 17.12.2011
comment
@Dani: Это правильно. Время добавления векторов будет O(n), если они разбросаны. Как и любой другой алгоритм. Но выделение массива имеет постоянный размер, как и O(1), даже если это занимает бесконечную память/время. - person Mooing Duck; 17.12.2011