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

Я ищу очень простой алгоритм для вычисления пересечения/отсечения многоугольника. То есть, учитывая полигоны P, Q, Я хочу найти polygon T содержащийся в P и Q, и желаю T быть максимальным среди всех возможных полигонов.

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

но для меня очень важно, чтобы алгоритм был простым (более дешевое тестирование) и желательно коротким (меньше кода).

edit: обратите внимание, я хочу получить многоугольник, который представляет собой пересечение. Мне не нужен только логический ответ на вопрос о том, пересекаются ли два полигона.

9 ответов


Я понимаю, что оригинальный плакат искал простое решение, но, к сожалению, на самом деле нет простого решения.

тем не менее, недавно я создал бесплатную библиотеку отсечения с открытым исходным кодом (написанную на Delphi, C++ и C#), которая клипирует все виды полигонов (включая самопересекающиеся). Эта библиотека довольно проста в использовании:http://sourceforge.net/projects/polyclipping/ .


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

одна реализация многоугольника отсечения, что вы можете использовать свою любимую поисковую систему, чтобы искать это Уайлера-Атертона. статья Википедии о Weiler-Atherton

Алан Мурта имеет полную реализацию многоугольника clipper GPC.

Edit:

другой подход заключается в том, чтобы сначала разделить каждый многоугольник на набор треугольников, с которыми легче иметь дело. The Теорема Двух Ушей Гэри Х. Мейстерс делает трюк. Это страница в McGill делает хорошую работу по объяснению подразделения треугольника.


Если вы используете C++, и не хочу создавать алгоритм самостоятельно, вы можете использовать импульс.Геометрия. Он использует адаптированную версию алгоритма Вейлера-Атертона, упомянутого выше.


вы не дали нам представление многоугольника. Поэтому я выбираю (больше похоже на предложение) один для вас:)

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

теперь, учитывая два полигона в этом представлении, вы можете вычислить пересечение как:

вычислить пересечение больших выпуклых многоугольников, чтобы сформировать большой многоугольник пересечения. Затем "вычитайте" пересечения всех меньших из них, чтобы получить список вычитаемых полигонов.

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

поскольку выпуклое пересечение многоугольника легко, это пересечение также должно быть легким.

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


вот подход, основанный на триангуляции, который довольно прост в реализации и может быть выполнен в O (N2).

КСТАТИ, O (N2) является оптимальным для данной проблемы. Представьте себе два многоугольника в форме Вилов, пересекающихся под прямым углом. Каждый из них имеет число сегментов, пропорциональное числу зубцов; число полигонов в пересечении пропорционально квадрату числа зубцов. стальные когти.

  1. во-первых, определить каждого полигона.

  2. сравните все треугольники из P попарно со всеми треугольниками из Q, чтобы обнаружить пересечения. Любая пара пересекающихся треугольников может быть разбита на меньшие треугольники, каждый из которых находится в P, Q или в пересечении. (Все, что вы использовали на шаге 1, можно использовать повторно, чтобы помочь в этом.) Хранить только треугольники, находящиеся на пересечении.

  3. вычислить соседи каждого треугольника, сравнивая их попарно, строят график смежности. Этот график будет содержать один связный подграф для каждого многоугольника в пересечении P и Q.

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


вот простой и глупый подход: на вход, дискретизировать свои полигоны в растровые. Пересечение и растровые изображения вместе. Чтобы получить выходные полигоны, проследите неровные границы растрового изображения и сгладьте их с помощью алгоритм полигональной аппроксимации. (Я не помню, дает ли эта ссылка наиболее подходящие алгоритмы, это только первый хит Google. Вы можете проверить один из инструментов для преобразования растровых изображений в векторные представления. Может быть, вы может ли вызвать их без повторного применения алгоритма?)

самая сложная часть будет отслеживание границ, Я думаю.

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


У меня нет очень простого решения, но вот основные шаги для реальные:

  1. сделать таможни двойной связанный список для вершин многоугольника и стыки. Используя std::list не будет делать, потому что вы должны поменять следующий и предыдущие указатели / смещения для специальной операции на узлы. Это единственный способ иметь простой код, и это даст хорошая производительность.
  2. найти точки пересечения, сравнивая каждую пару стыки. Отмечать это сравнение каждой пары ребер даст O (N2) время, но улучшит алгоритм для O (N·logN) будет легко после этого. Для какой-то пары ребра (скажем, a→b и C→d), точка пересечения найдена с помощью параметр (от 0 до 1) на ребро a→b, который задается tₐ=d₀/(d₀-d₁), где d₀ (С-А)×(В-А) и является d₁ (д-А)×(В-а). × есть двумерное поперечное произведение, такое как p×q=pₓ·qᵧ-pᵧ·qₓ. После того, как нашли tₐ, поиск точки пересечения использует ее как линейную интерполяцию параметр на отрезок a→b: P=a+tₐ (b-a)
  3. разбить каждое ребро, добавив вершины (и узлы в связанный список) где сегменты пересекаются.
  4. затем вы должны крест узлы в точках пересечения. Это операции, для которой необходимо выполнить двойной связью список. Вы должны поменять какую-то пару далее указатели (и обновление указатели соответственно).

тогда у вас есть необработанный результат многоугольника алгоритм разрешения пересечений. Обычно вы хотите выбрать какой-то регион в соответствии с номером намотки каждого региона. Поиск номер многоугольника намотки для объяснения этого.

Если вы хотите сделать алгоритм O(N·logN) из этого o(N2), вы должны сделать то же самое, за исключением того, что вы делаете это внутри алгоритма развертки линии. Ищите алгоритм Bentley Ottman. Внутренний алгоритм будет таким же, с той лишь разницей, что у вас будет уменьшенное количество ребер для сравнения внутри цикла.


то, как я работал над той же проблемой

  1. разбиение полигона на отрезки
  2. найти пересекающуюся линию с помощью IntervalTrees или LineSweepAlgo
  3. поиск закрытого пути с помощью GrahamScanAlgo найти замкнутый путь со смежными вершинами
  4. Перекрестная Ссылка 3. с DinicAlgo растворить их

Примечание: мой сценарий был другим, учитывая, что полигоны имели общую вершину. Но надеюсь, что это может помочь


Это может быть огромное приближение в зависимости от ваших полигонов, но вот один:

  • вычислить центр масс для каждого полигон.
  • вычислить min или max или average расстояние от каждой точки многоугольник к центру масс.
  • Если C1C2 (где C1/2-Центр первого/второго многоугольника) >= D1 + D2 (где D1/2-расстояние, вычисленное для первого/второго многоугольника), то два многоугольника "пересекаются".

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