Пересечение многоугольников (PSLG overlaying) — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Грани)
м (rollbackEdits.php mass rollback)
 
(не показаны 3 промежуточные версии 3 участников)
Строка 19: Строка 19:
 
<li>Ребра <tex>e_1</tex> и <tex>e_2</tex> пересекаются в общей вершине.</li>
 
<li>Ребра <tex>e_1</tex> и <tex>e_2</tex> пересекаются в общей вершине.</li>
 
<li>Вершина ребра <tex>e_1</tex> проходит через ребро <tex>e_2</tex>, разбивая его на два новых ребра.</li>
 
<li>Вершина ребра <tex>e_1</tex> проходит через ребро <tex>e_2</tex>, разбивая его на два новых ребра.</li>
<li>Ребра <tex>e_1</tex> и <tex>e_2</tex> имеют общий отрезок. Образуется новый отрезок.</li>
+
<li>Ребра <tex>e_1</tex> и <tex>e_2</tex> имеют общий отрезок. Образуется новое ребро.</li>
 
</ol>
 
</ol>
  
Строка 52: Строка 52:
  
 
==== Построение графа <tex>G</tex>====
 
==== Построение графа <tex>G</tex>====
Напомним, что в алгоритме заметающей прямой для пересечения отрезков мы искали ближайший отрезок, находящийся левее точки события. Эта информация необходима для построения графа <tex>G</tex>. Сначала создадим вершину для каждой границы. Далее необходимо построить ребра в графе, для этого найдем левую вершину каждой дырки и определим, какое ребро лежит слева от данной вершины. Для эффективности будем для каждого полуребра хранить ссылку на вершину графа, содержащую это полуребро. Таким образом, информация о гранях может быть восстановлена за время <tex>O(n + k)</tex>, после алгоритма заметающей прямой.
+
Напомним, что в алгоритме заметающей прямой для пересечения отрезков мы искали ближайший отрезок, находящийся левее точки события. Эта информация необходима для построения графа <tex>G</tex>. Сначала создадим вершину для каждой границы. Далее необходимо построить ребра в графе, для этого найдем левую вершину каждой дырки и определим, какое ребро лежит слева от данной вершины. Для эффективности будем для каждого полуребра хранить ссылку на вершину графа, содержащую это полуребро. Таким образом, информация о гранях может быть восстановлена за время <tex>O(n + k)</tex>, после алгоритма заметающей прямой. Также заметим, что информацию о структуре графа можно хранить в записях граней.
  
 
==== Маркировка граней ====
 
==== Маркировка граней ====

Текущая версия на 19:23, 4 сентября 2022

Введение

Пример работы алгоритма пересечения ППЛГ

Пересечением двух ППЛГ является ППЛГ, получающийся наложением двух исходных ППЛГ с созданием вершин в точках пересечения ребер. Определим пересечение двух ППЛГ [math]S_1[/math] и [math]S_2[/math] как ППЛГ [math]O(S_1, S_2)[/math], такой, что в нем существует грань [math]f[/math] тогда и только тогда, когда существуют грани [math]f_1[/math] в [math]S_1[/math] и [math]f_2[/math] в [math]S_2[/math] такие, что [math]f[/math] является наибольшим связным подмножеством [math]f_1 \cap f_2[/math]. Иначе говоря, пересечение двух ППЛГ — это разбиение плоскости с помощью ребер из [math]S_1[/math] и [math]S_2[/math].

Задача:
Необходимо построить РСДС для [math]O(S_1, S_2)[/math], имея РСДС для [math]S_1[/math] и [math]S_2[/math]. Кроме того, для каждой грани из [math]O(S_1, S_2)[/math] будем хранить ссылки на грани из [math]S_1[/math] и [math]S_2[/math], содержащие ее.


Алгоритм

Для начала скопируем ППЛГ [math]S_1[/math] и [math]S_2[/math] в новый РСДС. Далее необходимо преобразовать полученный РСДС, чтобы он соответствовал [math]O(S_1, S_2)[/math]. Отдельно рассмотрим преобразования вершин, полуребер и граней.

Вершины и полуребра

Алгоритм заметающей прямой

Алгоритм базируется на заметающей прямой, определяющей пересечения отрезков. Запускаем алгоритм на множестве отрезков, представляющих собой ребра из [math]S_1[/math] и [math]S_2[/math]. Напомним, что алгоритм поддерживает очередь событий [math]Q[/math] и текущий статус [math]T[/math] заметающей прямой. Также будем поддерживать ссылки между ребрами статуса и соответствующими полуребрами из РСДС. Поддерживаемый инвариант: в любой момент времени РСДС над заметающей прямой корректен.

Обработка точки события происходит следующим образом: сначала обновляем [math]Q[/math] и [math]T[/math] (как в алгоритме пересечения отрезков). Если оба ребра события принадлежат одному ППЛГ, переходим к следующему событию. В противном случае, необходимо модифицировать РСДС. Возможны следующие варианты пересечений (см. рисунок ниже):

  1. Вершина ребра [math]e_2[/math] проходит через ребро [math]e_1[/math], разбивая его на два новых ребра.
  2. Ребро [math]e_1[/math] пересекает ребро [math]e_2[/math]. Образуется четыре новых ребра.
  3. Ребра [math]e_1[/math] и [math]e_2[/math] пересекаются в общей вершине.
  4. Вершина ребра [math]e_1[/math] проходит через ребро [math]e_2[/math], разбивая его на два новых ребра.
  5. Ребра [math]e_1[/math] и [math]e_2[/math] имеют общий отрезок. Образуется новое ребро.
Варианты пересечений ребер [math]e_1[/math] и [math]e_2[/math]. Слева направо случаи (a) - (e).

Рассмотрим один из случаев, остальные обрабатываются аналогично. Пусть ребро [math]e[/math] из [math]S_1[/math] проходит через вершину [math]v[/math] из [math]S_2[/math]. Ребро [math]e[/math] заменяем двумя ребрами [math]e'[/math] и [math]e''[/math]. Два полуребра, соответствующих [math]e[/math], заменяются четырьмя полуребрами: два существующих полуребра будут исходить из концов [math]e[/math], а два новых полуребра — из [math]v[/math] (см. рисунок). Устанавливаем ссылки на близнецов для ребер [math]e'[/math] и [math]e''[/math]. Обновим ссылки на следующие полуребра для [math]h_1[/math] и [math]h_4[/math], пусть это будут [math]h_5[/math] и [math]h_6[/math], соответственно. Не забудем установить полуребра [math]h_1[/math] и [math]h_4[/math] в качестве предыдущих полуребер у [math]h_5[/math] и [math]h_6[/math]. Теперь обновим ссылки на полуребра, инцидентные вершине [math]v[/math]. Для этого сначала при помощи порядка обхода определим, между какими полуребрами [math]S_2[/math] находится [math]e[/math]. Рассмотрим полуребро [math]h_3[/math]: свяжем его с первым полуребром, видимым из [math]h_4[/math] при обходе по часовой стрелке и исходящем из [math]v[/math]. Полуребро [math]h_4[/math] должно быть связано с первым полуребром, идущим в [math]v[/math], при обходе против часовой стрелки. Аналогично обработаем [math]e''[/math].

Пересечение вершины [math]v[/math] и ребра [math]e[/math]
Модификация полуребер

Время работы

Большинство шагов алгоритма работают константное время. Определение соседних полуребер с [math]e'[/math] и [math]e''[/math] происходит за линейное время от степени вершины. Следовательно, обновление РСДС не увеличивает время работы алгоритма пересечения отрезков, поэтому сведения о вершинах и полуребрах для итогового РСДС могут быть вычислены за время [math]O(n \log{n} + k \log{n})[/math], где [math]n[/math] — сумма сложностей [math]S_1[/math] и [math]S_2[/math], [math]k[/math] — количество точек пересечения.

Грани

Поиск внешних границ и дырок. Вершины [math]v[/math] и [math]u[/math] – левые вершины циклов. Для полуребер [math]h_1[/math] и [math]h_2[/math] грань [math]f[/math] является внутренней, для полуребер [math]h_3[/math] и [math]h_4[/math] – внешней.

Необходимо получить информацию о гранях итогового РСДС: ссылка на полуребро внешней границы, список ссылок на полуребра дырок внутри грани, ссылка на грани из [math]S_1[/math] и [math]S_2[/math], содержащие новую грань. Также необходимо для полуребер установить ссылки на инцидентную грань. У каждой грани существует уникальная внешняя граница, поэтому количество граней будет на единицу больше, чем количество внешних границ (дополнительная граница ограничивает весь ППЛГ). Таким образом, каждой грани можно ставить в соответствие внешнюю границу данной грани (кроме внешней грани ППЛГ, для нее мы введем мнимую внешнюю границу). Следовательно, необходимо обойти все внешние границы ППЛГ и создать грань для каждой границы. Для того, чтобы определить, является цикл внешней границей или дыркой, рассмотрим самую левую вершину цикла [math]v[/math] (определяется обходом по циклу). Напомним, что полуребра ориентированы так, что инцидентная им грань лежит левее полуребра. С учетом этого, оценим угол внутри грани между полуребрами, инцидентными [math]v[/math]. Если угол меньше [math]180^\circ[/math], то цикл является внешней границей грани, в противном случае – лежит внутри грани. Данное свойство выполняется для вершины [math]v[/math], но может не выполняться для остальных вершин.

Для определения того, какие границы принадлежат одной грани, построим вспомогательный граф [math]G[/math], в котором каждая граница является вершиной. Также добавим вершину для мнимой границы внешней грани. Между двумя вершинами графа, соответствующим двум циклам РСДС, существует ребро тогда и только тогда, когда один цикл является границей дырки, а второй цикл является ближайшим слева к самой левой вершине первого цикла. Если левее самой левой вершины цикла нет полуребер, то соединим этот цикл с мнимой границей внешней грани ППЛГ.

Построение графа [math]G[/math]


Лемма:
Каждой компоненте связности графа [math]G[/math] соответствует множество циклов, инцидентных только одной грани.
Доказательство:
[math]\triangleright[/math]

Рассмотрим цикл [math]C[/math], ограничивающий дырку в грани [math]f[/math]. Грань [math]f[/math] лежит левее самой левой вершины [math]C[/math], поэтому [math]C[/math] должен быть связан с другим циклом грани [math]f[/math]. Следовательно, циклы одной компоненты связности графа [math]G[/math] ограничивают одну и ту же грань.

Покажем, что каждый цикл, ограничивающий дырку грани [math]f[/math], находится в одной компоненте связности с внешней границей грани [math]f[/math]. Предположим, что существует цикл, для которого это не выполняется. Пусть [math]C[/math] – самый левый такой цикл, причем его левая вершина также самая левая. По определению, существует ребро между [math]C[/math] и циклом [math]C'[/math], который лежит левее [math]C[/math]. Следовательно, [math]C'[/math] лежит в одной компоненте связности с [math]C[/math], причем [math]C'[/math] не является внешней границей [math]f[/math]. Получается, что [math]C'[/math] лежит левее [math]C[/math], что противоречит определению [math]C[/math].
[math]\triangleleft[/math]

Построение графа [math]G[/math]

Напомним, что в алгоритме заметающей прямой для пересечения отрезков мы искали ближайший отрезок, находящийся левее точки события. Эта информация необходима для построения графа [math]G[/math]. Сначала создадим вершину для каждой границы. Далее необходимо построить ребра в графе, для этого найдем левую вершину каждой дырки и определим, какое ребро лежит слева от данной вершины. Для эффективности будем для каждого полуребра хранить ссылку на вершину графа, содержащую это полуребро. Таким образом, информация о гранях может быть восстановлена за время [math]O(n + k)[/math], после алгоритма заметающей прямой. Также заметим, что информацию о структуре графа можно хранить в записях граней.

Маркировка граней

Необходимо для каждой грани РСДС определить, какие грани из [math]S_1[/math] и [math]S_2[/math] содержат ее. Рассмотрим вершину [math]v[/math] грани [math]f[/math]. Если [math]v[/math] является пересечением ребра [math]e_1[/math] из [math]S_1[/math] и ребра [math]e_2[/math] из [math]S_2[/math], то по указателям на инцидентные грани можно определить, какие грани содержат данные ребра. Если [math]v[/math] является вершиной [math]S_1[/math], то мы можем определить только грань из [math]S_1[/math], содержащую [math]v[/math]. Необходимо научиться определять грань из [math]S_2[/math], содержащую [math]v[/math]. То есть для каждой вершины из [math]S_1[/math] мы должны знать, какая грань из [math]S_2[/math] содержит данную вершину, и наоборот. Это также делается заметающей прямой.

Итог

Вход: два ППЛГ [math]S_1[/math] и [math]S_2[/math], представленные в виде РСДС.

Выход: пересечение [math]S_1[/math] и [math]S_2[/math], представленное в виде РСДС [math]D[/math].

  1. Скопируем [math]S_1[/math] и [math]S_2[/math] в новый РСДС [math]D[/math].
  2. Найдем пересечения ребер из [math]S_1[/math] и [math]S_2[/math] с помощью заметающей прямой. При обработке события будем обновлять [math]D[/math], если событие затрагивает [math]S_1[/math] и [math]S_2[/math]. Также для вершины события сохраним информацию о ближайшем полуребре слева.
  3. Найдем граничные циклы в [math]O(S_1, S_2)[/math], обходя [math]D[/math].
  4. Построим граф [math]G[/math], вершинам которого соответствуют циклы, соединив ребрами дырки и циклы, ближайшие слева к левым вершинам дырок.
  5. Для каждой компоненты связности графа [math]G[/math]: пусть [math]C[/math] – внешняя граница компоненты связности, ограничивающая грань [math]f[/math]. Создать запись для этой грани, установить ссылку на полуребро внешней границы грани и ссылки на полуребра дырок грани. Также для всех полуребер грани установить ссылки на инцидентную грань.
  6. Для каждой грани [math]f[/math] из [math]O(S_1, S_2)[/math] установить ссылки на грани из [math]S_1[/math] и [math]S_2[/math], содержащие [math]f[/math].

Общее время работы

Теорема:
Пусть [math]S_1[/math] имеет сложность [math]n_1[/math], [math]S_2[/math] имеет сложность [math]n_2[/math], [math]n = n_1 + n_2[/math]. Пересечение [math]S_1[/math] и [math]S_2[/math] может быть построено за время [math]O(n \log{n} + k \log{n})[/math], где [math]k[/math] – количество точек пересечения [math]S_1[/math] и [math]S_2[/math].
Доказательство:
[math]\triangleright[/math]
Копирование двух РСДС в один занимает [math]O(n)[/math] времени, заметающая прямая работает [math]O(n \log{n} + k \log{n})[/math] времени по лемме. Создание граней работает линейное время от сложности [math]O(S_1, S_2)[/math]. Маркировка граней РСДС работает за [math]O(n \log{n} + k \log{n})[/math].
[math]\triangleleft[/math]

См.также

Источники информации

  • de Berg, Cheong, van Kreveld, Overmars. Computational Geometry, Algorithms and Applicants, 2008. pp. 33-39