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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

Edit:

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

Ответ 3

Если вы используете С++ и не хотите самостоятельно создавать алгоритм, вы можете использовать Boost.Geometry. Он использует адаптированную версию упомянутого выше алгоритма Weiler-Atherton.

Ответ 4

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

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

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

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

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

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

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

Ответ 5

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

BTW, O (N 2) является оптимальным для этой задачи. Представьте себе два многоугольника в форме лопаток, пересекающихся под прямым углом. Каждый из них имеет несколько сегментов, пропорциональных числу зубцов; число полигонов в пересечении пропорционально квадрату числа зубцов.

  • Сначала триангулируем каждый многоугольник.

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

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Если вы хотите сделать O (N · logN) алгоритм из этого O (N²), вы должны сделать то же самое, кроме того, что вы делаете это внутри алгоритма развертки линии. Найдите алгоритм Бентли Оттмана. Внутренний алгоритм будет таким же, с той лишь разницей, что у вас будет меньшее количество ребер для сравнения внутри цикла.

Ответ 8

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

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

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

Ответ 9

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

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

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