Straight skeleton

Материал из Викиконспекты
Версия от 18:04, 3 декабря 2014; Shersh (обсуждение | вклад) (Алгоритм с изпользованием SLAV)
Перейти к: навигация, поиск
Эта статья находится в разработке!

Существует целый класс структур типа skeleton, которые описывают базовые топологические свойства объектов. Структура straight skeleton была придумала Oswin Aichholzer. Она используются в различных практических задачах (проектирование крыш для зданий) и для доказательства некоторых теорем[1].

Топологические свойства

Определение:
Straight skeleton (Angular Bisector Network, ABN) полигона без самопересечений называется планарный граф, определяющий разбиение полигона на регионы, границами которых являются стороны полигона, биссектрисы углов и отрезки, соединяющие вершины straight skeleton, образовавшиеся в результате сжатия полигона.
Straight skeleton definition.png

Опишем подробней, как получается такое разбиение. Мы можем представить, будто все стороны прямоугольника параллельно двигаются внутрь с одинаковой постоянной скоростью, то есть многоугольник как бы сжимается внутрь. Тогда вершины будут двигаться вдоль биссектрис , а точки пересечения биссектрис будут соединять совпавшие участки сторон прямоугольника в конце движения. В каждый момент времени от начала движения рёбер мы получаем слоистую структуру (рис 1.). На рис. 2 синим цветом выделен straight skeleton — множество отрезков, образованных точками пересечения при движении сторон полигона. Чем-то структура похожа на строение крыши в домах (рис. 3). И для решения этой задачи как раз straight skeleton и может применяться: по стенам здания необходимо спроектировать его крышу.

Проектирование крыши здания по готовым стенам

Процесса стягивания многоугольника продолжается до тех пор, пока происходят его топологические изменения, то есть меняется число вершин в стянутом многоугольнике, и таким образом появляются новые вершины дерева straight skeleton. Существуют два типа изменений, в ходе которых образуются новые вершины дерева:

  • Edge event — данное изменение происходит, когда сторона многоугольника полностью стягивается, делая соседние стороны инцидентными.
  • Split event происходит, когда ребро разбивается на два новых ребра, исходящих из точки преломления старого. Такое событие происходит на биссектрисе вогнутой вершины многоугольника. И тогда стягиваемая многоугольником область разбивается на две непересекающиеся многоугольные области.

На рисунке edge eventы изображён красным кругом, а split eventы — чёрным прямоугольником.

Sk example1.jpg

Таким образом, eventы соответствуют внутренним вершинам straight skeleton, гранями являются области многоугольника, заметаемые сторонами многоугольника в процессе стягивания, дуги straight skeleton соединяют либо две внутренние вершины либо внутреннюю вершину с листом — вершиной многоугольника.

Стоит также отметить, что в общем случае split eventы могут быть нетривиальными. На рисунке ниже в случае (c) в вершине p совпали split event из вершины u и edge event ребра e, а в случае (d) совпали два split eventа вершин u1 и u2. Случаи (a) и (b) — простые edge и split eventы.

Event example.png

Свойства Straight skeleton

Из процесса построения straight skeleton следует, что он является планарным графом. Ранее уже упоминалось, что он также является деревом. Будем обозначать straight skeleton простого полигона без самопересечений P, в котором n вершин, как S(P). Тогда справедливы следующие леммы:

Лемма (1):
S(P) является деревом, содержит n граней, не более n2 внутренние вершины и не более 2n3 рёбер.
Доказательство:

Каждая грань f(e) начинает образовываться во время стягивания ребра e, и даже если на ребре произошёл split event, сама грань не могла разделиться. Построение грани f(e) завершается, когда ребро e полностью стягивается. И это ребро дальше не может появиться снова, поэтому граней в S(P) столько, сколько сторон в многоугольнике, то есть ровно n.

То, что S(P) является деревом, легко доказывается по индукции. База верна, когда внутренняя вершина всего одна. Тогда у straight skeleton листьями будут вершины многоугольника. Такой граф очевидным образом будет деревом. Если в straight skeleton k внутренних вершин, то рассмотрим самый первый edge event. Он закончился в какой-то внутренней вершине straight skeleton, у неё есть смежные листья — вершины, инцидентные этому ребру, — и из неё достижимы другие straight skeletonы, с не более чем k1 внутренними вершинами, и они являются деревьями по предположению индукцию. Тогда получаем, что S(P) для k вершин тоже будет деревом.

Внутренние вершины в straight skeleton имеют степень не меньше 3 — простой перебор всех случаев eventов (степень будет больше, если в одной вершине совпало несколько событий). Так как S(P) имеет n листьев, то внутренних вершин будет не больше n2, а так как S(P) является деревом, то рёбер у него будет не более 2n3.

Замечание: если мы рассмотрим straight skeleton в какой-то момент времени, то он вполне может содержать циклы (это видно на одном из рисунков выше). Однако его конечная структура будет деревом.

Алгоритм с изпользованием SLAV

Далее будет описан алгоритм, придуманный Petr Felkel, который строит straight skeleton за время O(nm+nlogn), где n — общее число вершин в полигоне, m — число вогнутых вершин в полигоне. Немного модифицированный этот алгоритм используется в открытой библиотеке вычислительной геометрии CGAL[2]. Однако этот алгоритм всё равно ещё достаточно медленный. В реальной жизни используют его модификации или более сложные алгоритмы.

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

Выпуклый полигон

В случае выпуклого многоугольника возникают только edge eventы по определению. Поэтому просто алгоритм можно описать следующим образом: найдём точки пересечения биссектрис многоугольника для каждой вершины со всеми соседними вершинами, возьмём такую точку, в которой произойдёт самый первый edge event, добавим полученную вершину в straight skeleton, соеденим её с вершинами ребра, которое исчезло в процессе текущего edge eventа, а потом перестроим полигон, создав новую вершину и подвинув все остальные вдоль биссектрис на одинаковое расстояние. Будем продолжать этот процесс до тех пор, пока многоугольник не превратится в треугольник.

Теперь реализуем этот алгоритм более эффективно. Для этого мы будем использовать специальную структуру данных — SLAV (set of circular lists of active vertices). Эта структура хранит цикл всех вершин для внешней грани, а так же цикл для каждой дыры многоугольника и для всех многоугольников, возникающих в процессе построения S(P). В данном случае у нас будет просто LAV циклический список всех вершин многоугольника.

Эффективный алгоритм

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

Шаг 1. Инициализация:

(a) Поместим все вершины многоугольника V1,V2Vn в двусвязный циклический список в порядке обхода вдоль контура. Все вершины в LAV считаются активными сейчас.
(b) Для каждой вершины Vi в LAV добавим указатели на инцидентные рёбра ei1=Vi1Vi и ei=ViVi+1, а также найдём луч биссектрисы bi.
(c) Для каждой вершины Vi найдём ближайшее пересечение биссектрисы bi лучами bi1 и bi+1. Если это пересечение существует, то положим его в приоритетную очередь согласно L(ei) — расстоянию от точки пересечения до одного из рёбер, инцидентных вершине Vi. Для каждой точки пересечения Ii будем так же хранить два указателя на вершины Va и Vb — начала лучей биссектрис, которые пересекаются в точке Ii. Эти указатели понадобятся в будущем, когда нужно будет определять соответствующие вершинам рёбра ea,eb (см. рисунок ниже).

Шаг 2. Следующие действия выполняются в цикле, пока приоритетная очередь не пустая:

(a) Извлечём точку пересечения I из приоритетной очереди.
(b) Если вершины Va и Vb, соответствующие данной точке пересечения помечены как обработанные, то переходим к следующей итерации цикла шага 2. Это означает, что ребро между данными вершинами полностью стянулось (обработанные вершины и стянутые рёбра помечены крестом на рисунке ниже).
(c) Если осталось всего три вершины Va,Vb,Vc, то добавим в straight skeleton рёбра IVa,IVb,IVc. В случае выпуклого многоугольника в этом месте можно завершить алгоритм. Но в общем случае нужно будет перейти к началу цикла снова.
(d) Добавим в straight skeleton рёбра IVa,IVb.
(e) Теперь необходимо модифицировать LAV (детали на рисунке ниже):
  • пометим вершины Va и Vb как обработанные (напомню, что они обозначаются крестом на рисунке к данному алгоритму),
  • создадим новую вершину V в точке пересечения I (отмечена квадратиком на рисунке),
  • добавим вершину V в LAV, то есть между предыдущем к Va и следующим к Vb узлами,
  • добавим вершине V указатели на соответствующие рёбра ea и eb.
(f) Посчитаем дополнительные величины для вершины V:
  • луч биссектрисы b между рёбрами ea и eb,
  • точки пересечения луча b с соседями V в LAV, как в шаге 1c
  • сохраним ближайшие точки пересечения в приоритетной очереди.

Заметим, что нам не нужно пересчитывать все расстояния в очереди приоритетов. Если мы стянем полигон до первого события исчезания ребра, то относительный порядок остальных событий не изменится. Нам необходимо только вставить новые точки пересечения в правильные места. Это можно эффективно сделать, если использовать структуру данных типа skip list.

Частным случаем в алгоритме может быть совпадение нескольких edge eventов в одной точке. Эти совпадения добавляются в шагах 1c и 2f, но могут быть относительно легко обработаны в шаге 2b. Также может случиться, что какие-то рёбра не стянулись в итоге в одну вершину, а слились. Такое возможно, если какие-то стороны полигона были изначально параллельны (этот случай легко увидеть на прямоугольнике, не являющемся квадратом). TODO: И что делать?(

Невыпуклый полигон

Алгоритм построения с помощью Motorcycle graph

Рассмотрим алгоритм построения straigt skeleton на основе мотографов.

TODO: Алгоритм на мотографах

Другие алгоритмы

Существует простой в понимании и реализации алгоритм для построения straigt skeleton на основе триангуляции, который работает за время O(n3logn)[3]. Aichholzer смог обобщить этот алгоритм для построения straigt skeleton произвольного планарного графа[4]. Также автором в его оригинальной статье был представлен алгоритм построения данной структуры, базирующийся на понятии волнового фронта (англ. wavefront). Этот алгоритм может быть реализован за время O(n3) с использованием O(n) памяти либо с использованием приоритетной очереди за время O(n2logn) и O(n2) памяти[5]. Известен алгоритм построения straight skeleton для монотонных полигонов за время O(nlogn) с использованием O(n) памяти[6].

В данном конспект был (P.S. точнее, ещё будет) представлен алгоритм на основе мотографов, который придумали Stefan Huber и Martin Held. Они говорят, что даже смогли реализовать этот алгоритм, но код нигде не выкладывали.

Примечания

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