Ивановский Сергей Алексеевич, Преображенский Алексей Семенович, Симончик Сергей Константинович
алгоритмы вычислительной геометрии. выпуклые оболочки в трехмерном пространстве
1. ВВЕДЕНИЕ
Выпуклые оболочки в трехмерном пространстве используются в различных приложениях. Например, они используются для ускорения нахождения коллизий трехмерных объектов в компьютерной анимации. Предположим, требуется проверить, пересекаются ли два объекта Р1 и Р2 сложной формы. Если в большинстве случаев объекты не пересекаются, то следующая стратегия дает выигрыш. Сначала аппроксимируем объекты Р1 и Р2 более простыми объектами Р1* и Р2* соответственно, которые содержат в себе исходные объекты. Затем для проверки пересечения Р1 и Р2 сначала проверяем, пересекаются ли Р1 и Р2 . Только в случае, когда Р1* и Р2* пересекаются, проверяем, пересекаются ли исходные объекты.
Каким должен быть аппроксимирующий объект? С одной стороны, аппроксимирующие объекты должны быть простыми, чтобы проверки пересечения были дешевыми. С другой стороны, простая аппроксимация не дает возможности хорошо приблизить исходный объект, вследствие чего с большой вероятностью придется выполнять проверку пересечения исходных объектов.
В случае ограничивающих сфер проверка пересечения сфер довольно проста, но для многих объектов сферы не дают хорошей аппроксимации. При использовании выпуклых оболочек объектов в качестве их аппроксимации проверка пересечения более сложна, чем для сфер, но зато большинство объектов лучше аппроксимируются, и факт отсутствия пересечений близлежащих объектов устанавливается с меньшими затратами (см. рис. 1).
Алгоритмы построения выпуклых оболочек на плоскости были описаны в [1, 2]. Некоторые из них могут быть обобщены на трехмерный случай.
Рис. 1. Аппроксимация объектов ограничивающими сферами и выпуклыми оболочками
ГрЭИЬ
Рис. 2. Пример многогранника
2. ОСНОВНЫЕ ОПРЕДЕЛЕНИЯ
По определению многогранник - это тело, ограниченное (со всех сторон) конечным числом плоскостей. Многоугольники, образованные пересечением этих плоскостей, называют гранями, их стороны - ребрами, а их вершины - вершинами многогранника (см. рис. 2). Поверхность многогранника, образованную гранями, также называют многогранником (обычно из контекста ясно, идет ли речь о поверхности или о теле).
Более точно определим многогранник [3] как множество (конечное) многоугольников, связанных таким образом, что, во-первых, при каждом ребре сходятся (под некоторым углом) два и только два многоугольника, а
во-вторых, от всякого многоугольника можно прейти к другому, переходя через ребра.
В соответствии с данным определением, фигуры, представленные на рис. 3 а-д, являются многогранниками, а на рис. 3 е-з - нет.
Выпуклым называется многогранник, расположенный по одну сторону каждой из своих граней. Выпуклый многогранник можно положить на плоскость (например, на плоскость стола) на любую из его граней. Многогранники на рис. 3 а-в выпуклые, а на рис. 3 г, д - нет. Грань выпуклого многогранника представляет собой выпуклый многоугольник.
Как и в двумерном случае, выпуклая оболочка СЯ(0 множества точек Q в трехмерном пространстве - это наименьшее выпуклое множество, включающее в себя все эти точки. Выпуклая оболочка конечного множества точек в трехмерном пространстве является выпуклым многогранником, вершины которого принадлежат исходному множеству точек.
3. СЛОЖНОСТЬ ПРЕДСТАВЛЕНИЯ ВЫПУКЛОЙ ОБОЛОЧКИ
Многогранник может быть
полностью определен перечислением его вершин, ребер и граней. Однако удобство и эффективность
работы с таким объектом в алгоритмах зависит от способа представления его в виде некоторой структуры данных. Количественно сложность такого представления можно охарактеризовать объемом необходимой памяти, который будет зависеть от степени сложности многогранника, то есть от количества его вершин п, количества его ребер т и количества его граней I. Оказывается, что эти три параметра выпуклого многогранника в трехмерном пространстве связаны формулой Эйлера п - т + I = 2. Геометрическое доказательство этого факта можно найти, например, в [3], [4].
Удобно использовать преобразование выпуклого многогранника в связный плоский (планарный) граф (такой граф укладывается на плоскости без самопересечений, при этом все его ребра - прямолинейные отрезки [5]). Для этого выберем некоторую грань многогранника. Будем рассматривать ее как верхнюю. Возьмем внутреннюю точку этой грани (выпуклого многоугольника) и поднимемся из нее вверх по нормали к грани до некоторой точки р. Рассмотрим множество лучей, исходящих из точки р и проходящих через вершины многогранника (считаем, что точка р выбрана так, чтобы все эти лучи пересекали выбранную верхнюю грань в ее внутренних точках, кроме лучей проходящих через вершины этой грани). Рассмотрим точки пересечения этих лучей с плоскостью, параллельной выбранной гра-
ни и лежащей ниже многогранника или касающейся его. Полученные точки будем считать вершинами графа. Тогда его ребрами будут отрезки, являющиеся проекциями ребер многогранника (см. пример на рис. 4).
Полученный плоский прямолинейный граф разбивает плоскость на множество выпуклых многоугольников (граней разбиения плоскости). Поскольку формула Эйлера справедлива для произвольного связного плоского графа [5], то она с учетом описанного преобразования справедлива и для выпуклого многогранника. Более того, из формулы Эйлера (с учетом того, что каждая грань и вершина многогранника имеет, по крайней мере, три инцидентных ребра) следуют соотношения, связывающие попарно параметры выпуклого многогранника [5]:
т < 3п - 6; I < 2п - 4; п < 31 - 4; т < 31 - 6.
Таким образом для полного описания (представления) многогранника с п вершинами достаточно объема памяти О (п).
Преобразование выпуклого многогранника в плоский граф дает возможность использовать для представления выпуклого многогранника структуры данных, подходящие для представления планарного графа (такие, как списки смежности или реберный список с двойными связями [6]). Представление с помощью реберного списка рассмотрено в приложении. Важно от-
Рис. 4. Пример преобразования выпуклого многогранника (куба) в плоский граф: а) проектирование куба: р - центр проекции; грань куба (1, 2, 3, 4) лежит в плоскости проекции; б) плоский прямолинейный граф; верхняя грань куба (5, 6, 7, 8) преобразуется во внешнюю (бесконечную)
область - вне квадрата (5*, 6*, 7*, 8*)
б 8
5
Рис. 5. Представление а) куба и б) плоского графа ориентированными гранями и ориентированными ребрами
метить, что при таком представлении (и использовании его в алгоритмах) полезно ввести определенную ориентацию ребер и граней. Поясним это с помощью рис. 5.
Каждая грань куба (как отдельный многоугольник) ориентирована так, что нормаль к ней направлена вне куба. На рис. 5 а показаны нормали к граням/1 и /2. Если смотреть на грань со стороны нормали, то ребра ориентированы в порядке обхода против часовой стрелки. Представление граней изображенного на рис. 5 куба списками вершин и ориентированных ребер показано в табл. 1.
Каждое ребро куба представлено в гранях дважды. Причем его ориентация различна в соседних гранях (в списках ориентированных ребер). Например, неориентированное ребро куба {Р6, Р7} представлено в гранях / и /2 ориентированными ребрами [ Р6, Р7] и [ р7,р6], соответственно. Общее количество всех ориентированных ребер равно удвоенному количеству ребер в выпуклом многограннике.
Отметим, что на рис. 5 б плоский граф изображен при взгляде на плоскость проекции сверху, поэтому его грани обходятся по часовой стрелке (за исключением внешней грани/1).
Выпуклый многогранник называется симплициальным, если каждая его грань представляет собой треугольник. Работа с такими многогранниками в алгоритмах осуществляется более эффективно. Произвольный выпуклый многогранник можно представлять симплициальным, если произвести триангуляцию его граней (выпуклых многоугольников). В описываемых далее алгоритмах выпуклая оболочка будет строиться в виде симплициального многогранника.
После того как установлена сложность представления выпуклой оболочки, естественно задаться вопросом, какова нижняя оценка сложности построения выпуклой оболочки в трехмерном пространстве? Так как любая совокупность точек в двумерном
Табл. 1
7
5
1
6
Грань Список вершин Список ориентированных ребер
/1 (Р5, Р6, Р7, Р8) ([Р5, Р6], [Р6, Р7], [Р7, Р8], [Р8, Р5])
/2 (Р2, Р3, Р7, Р6) ([Р2, Р3], [Р3, Р7], [Р7, Р6], [Р6, Р2])
/3 (Р3, Р4, Р8, Р7) ([Р3, Р4], [Р4, Р8], [Р8, Р7], [Р7, Р3])
/4 (Р1, Р5, Р8, Р4) ([Р1, Р5], [Р5, Р8], [Р8, Р4], [Р4, Р1])
/5 (РЬ Р2, Р6, Р5) ([РЬ Р2]' [Р2' Р6] [Р6, Р5] [Р5, Р1])
/6 (Р1, Р4, Р3, Р2) ([Р1, Р4], [Р4, Р3], [Р3, Р2], [Р2, Р1])
пространстве тривиальным образом вкладывается в трехмерное пространство, то нижняя оценка сложности построения выпуклой оболочки в трехмерном пространстве равна O(n log n). Ниже будут представлены алгоритмы, достигающие нижней оценки.
4. АЛГОРИТМ «ЗАВОРАЧИВАНИЯ ПОДАРКА»
Основная идея алгоритма [7] заключается в последовательном построении выпуклой оболочки путем переходов от одной грани (уже существующей) к смежной с ней новой грани, примерно так, как это происходит при заворачивании выпуклого ограниченного плоскими гранями объекта в лист бумаги (см. рис. 6). По сути это та же идея, что и в алгоритме Джарвиса, рассмотренном ранее [1], но приспособленная к трехмерному случаю.
Основой метода является то, что в выпуклой оболочке любое ребро является общим в точности для двух граней, и две грани имеют общее ребро тогда и только тогда, когда ребро определяется общим под-
множеством из двух вершин для множеств вершин, определяемых этими гранями.
На каждом шаге строится связная часть выпуклой оболочки (из одной грани можно пройти в другую, если у них есть общее ребро). Выбирается грань / из текущей построенной части выпуклой оболочки, у этой грани выбирается ребро е такое, что другая смежная по этому ребру грань еще не найдена. Строится плоскость р, содержащая в себе грань /. Построенная плоскость «наклоняется» через ребро е к множеству точек так, чтобы выбрать «подходящую» точку р. На базе ребра е и точки р строится новая грань выпуклой оболочки, и если это не последняя грань, то алгоритм продолжает работу. Как и в двумерном случае, подходящую точку р можно охарактеризовать наименьшим углом поворота плоскости р (см. рис. 7).
Формализуем выходные данные алгоритма. Выпуклая оболочка будет представляться симплициальным многогранником. В данном алгоритме нет необходимости использовать в явном виде реберный список с двойными связями. Можно обойтись заданием связанных друг с другом множества вершин, множества ориентированных ребер и множества ориентированных треугольных граней. Как и в разделе 3, будем считать, что вершины ориентированной грани выпуклой оболочки перечислены против часовой стрелки, если смотреть на грань извне.
Рис. 6. Несколько шагов построения выпуклой оболочки методом заворачивания подарка
Рис. 7. Иллюстрация выбора грани, определяемой ребром е и точкой р = р4 и образующей наименьший выпуклый угол с плоскостью, содержащей грань /
Далее при записи действий алгоритма нам понадобится понятие векторного произведения трехмерных векторов. Векторным произведением [8] вектора а и вектора b называется вектор с , обозначаемый как с = [а, b] и удовлетворяющий трем условиям:
1. Длина вектора |с| = |а||b| sin j (есть площадь параллелограмма, см. рис. 8);
2. Вектор с ортогонален к каждому из векторов а и b ;
3. Вектор с направлен так, что тройка векторов а, b и с является правой (см. рис. 8).
В декартовых координатах с = [а, b] =
= (yazb — ybZa' ZaXb — ZbXa' Xayb — Xbya)
С помощью векторного произведения легко связать нормаль ориентированной грани и ее ориентированные ребра. Например, на рис. 9 нормаль грани f1 тетраэдра равна [ab, bd ] и направлена на нас, а нормаль грани f2 равна [bc, cd].
Определим для плоскости р верхнее полупространство как {pе R3| (p—p0,n)> о}, где p0 ер и n - нормаль р и (a, b) обозначает скалярное произведение векторов а и b . Нижнее полупространство определим как R3 \ верхнее полупространство. Рис. 10 иллюстрирует эти определения.
После этих предварительных определений рассмотрим набросок алгоритма (листинг 1).
Поясним и проанализируем основные шаги алгоритма.
Построение начального ребра (строка 2), принадлежащего выпуклой оболочке CH (P)
может быть сделано за время О(п) следующим образом.
1. Найти точку р0 е Р, заведомо являющуюся вершиной выпуклой оболочки, в качестве такой точки можно взять наименьшую точку, если на точках определен следующий порядок: для точек р1, р2 е Я3 р1 < р2, если вектор р1 лексикографически меньше векторар2 (сложность шага О(п)).
2. Построить плоскость Р, р0 ер, р || ТОХ (сложность шага О(п)).
3. Плоскость Р наклоняется через прямую, параллельную оси ОУ и проходящую через точку р0, до тех пор, пока не будет встречена первая точка р1 (сложность шага О (п)).
4. Сформировать множество точек Q, принадлежащих плоскости р (р0, р1 е Q) за время О(п).
5. Найти любое ребро выпуклой оболочки множества точек Q (аналогично тому, как это сделано в алгоритме Джарвиса) за время О(п). Это ребро будет ребром СН (Р).
В процессе работы алгоритма используется стек ориентированных ребер. Таким образом, если пара вершин а и Ь определяет ребро СН (Р), то в стек на разных шагах алгоритма будут добавлены ориентированные ребра [а,Ь] и \Ь,а]. Ориентированность ребер позволяет однозначно определить грань выпуклой оболочки и сориентировать её правильным образом.
В 5-ой строке алгоритма нормаль плоскости р определяется как [аЬ, api ] (ребро (а, Ь) ориентировано в порядке обхода вер-
Рис. 8. Векторное произведение с = [а, Ь] вектора а и вектора Ь . Со стороны вектора с угол (р (0 < (р < р ) отсчитывается против часовой стрелки
Рис. 9. Ориентированные ребра, инцидентные грани/1, есть [а,Ь], [Ь,<1] и [й,а],
а нормаль /1 равна [аЬ, Ьd ]; грани /2 инцидентны ориентированные ребра [Ь,с], и [<1,Ь], а нормаль/2 равна [Ьс,cd]
шин содержащейся в плоскости p грани против часовой стрелки, если смотреть извне на выпуклую оболочку). Таким образом, шаги 5 и 6 могут быть сделаны за время O(n).
На шаге 7 строится двумерная выпуклая оболочка найденного множества точек T (в плоскости p могут лежать более трех точек). Этот шаг можно сделать за время
O \|T| \ Textreme
), где Textreme - множество крайних точек выпуклой оболочки множества T. Для достижения такой оценки можно использовать алгоритм Джарвиса, либо асимптотически оптимальный алгоритм со сложностью O (\Т|log (\Textreme I)). При анализе общей сложности алгоритма важно, чтобы на этом шаге использовался алгоритм, сложность которого определяется числом вершин построенной оболочки Textreme.
После построения двумерной выпуклой оболочки CH (T) определен обход её вершин (против часовой стрелки, если смотреть извне). Следовательно, можно выделить и сориентировать компланарные треугольные грани (строка 8), принадлежащие p (см. рис. 11), за линейное по количеству этих граней время (таких граней бу-
Рис. 10. Точка p лежит в верхнем полупространстве плоскости Р (здесь (Р _ Po'и) > 0), а точка p* - в нижнем полупространстве (здесь (p * -p0, n) < 0); для всех точек q плоскости Р справедливо (p - p0, n) = 0
дет \CH (Т)| - 2). Также за линейное время можно выделить и добавить в стек ориентированных ребер крайние ребра CH (Т) для дальнейшего построения оболочки (исключая ребро (a,b) их будет |CH(Т) -1).
Узнать (строка 9), было ли ориентированное ребро ранее добавлено в стек, мож-
Листинг 1. Algorithm GIFT-WRAPPING(P) ® CH(P) Вход. Исходное множество точек P = {pt |p, = (xi, yi), i = [1..n] }.
Выход. Выпуклая оболочка множества точек CH(P), заданная набором треугольных граней.
1 инициализировать выпуклую оболочку пустой
2 найти некоторое исходное ребро (с произвольной ориентацией) результирующей выпуклой оболочки и добавить его в стек ориентированных ребер S
3 while Size(S) > 0 do
4 e — Pop(S)
5 найти такую плоскость p, содержащую CH(e и p), что p e P и все точки pt e P принадлежат нижнему полупространству плоскости Р; при этом если e = [a, b], то нормаль p определяется как [ab, api ]
найти множество T = {p e P|p ep} построить двумерную выпуклую оболочку CH (T)
создать новые треугольные грани, определяемые CH (T), и добавить их в CH (P)
обойти новые крайние ребра CH(P) (ребра CH(T)), и добавить
их в стек S, если они еще не были добавлены ранее end-do
10 11
вернуть построенную выпуклую оболочку CH (P)
ИНФОРМАТИКА 9
algorithm UPPERHULL (Q) ® верхняя оболочка
£ И < n + £ ^'extreme ^ O(n) •
• T 11 +
I extreme I '
i)-
Рис. 11. В плоскости ж лежат точки T = {a,b,tl,t2,t3,t4,t5}. Их выпуклая оболочка есть CH (T) = {a, b, tl, t5, t3}.
Созданы треугольные грани = (b, tl, t5), F2 = (b, t5, t3), F3 = (b, t3, a), F1, F2, F3 еж.
В стек добавлены ориентированные ребра
ei = (b О , e2 = Op t5) > e2 = (t5, t3) > e4 = (t3, a)
но за время O (log(n)), используя сбалансированное дерево поиска [9] и введя произвольный порядок на ребрах. Заметим, что поскольку необходимо только добавлять ребра в структуру данных и искать в ней, то в качестве такой структуры можно использовать хеш-таблицу [9].
Общее количество выполнений цикла while (строки 3-10) равно количеству граней F, принадлежащих разным плоскостям многогранника.
Оценим общую сложность алгоритма. Обозначим за T1 множество T, а за T^xtreme множество Txcr,me на г-ом шаге работы алгоритма (другими словами при нахождении г-ой плоскости). Поиск множества T1 можно выполнить за O(n) операций (строки 5-6). Построение двумерной выпуклой оболочки делается за O (|т'| • |ТгехГгете ) операций (строка 7). Создание новых O (т^х1гете ) граней требует линейного времени (строка 8), поиск и добавление O(\Textreme|) ориентированных ребер в стек требует O ((extreme \ ' log(F) ).
Пусть L - количество ребер в CH (P), тогда F
i\TLeme\ = 2• Lе O(F) и
Общее время работы:
X (O(n) + O ((
i=\
+ O ) + O (eme\' l0g( F)) )<
F
< O(nF) + XO(|Ti| • \Tlxreem\) + O(F) + i=1
+ O (Flog(F)) < O(nF) + O (nF)< O(nF)
Итак, в среднем время работы алгоритма составляет O(nF), а в худшем случае, когда все точки множества оказываются на оболочке, - O(n2) •
5. АЛГОРИТМ РАЗДЕЛЕНИЯ И СЛИЯНИЯ
Нижняя оценка для задачи построения выпуклой оболочки в трехмерном пространстве такая же, как и в двумерном случае: W(n log n). Алгоритм сбалансированного разделения и слияния, основанный на методе «разделяй и властвуй» (лат. «Divide et impera», англ• «divide-and-conquer»), был предложен в [10] и достигает нижней оценки. Позже в алгоритм были внесены важные корректировки [6], а в [11] описана реализация алгоритма. Алгоритм является обобщением алгоритма сбалансированного разделения и слияния «с мостиками» для плоского случая [2].
Принцип алгоритма такой же, как и в двумерном пространстве: отсортировать множество точек по x координате, разбить его на два линейно разделимых множества, рекурсивно построить выпуклые оболочки в каждом из множеств и затем слить их в одну оболочку. Слияние может быть сделано за O(n) операций, и, таким образом, общая сложность составляет O(n log n).
В листинге 2 приводится укрупненный псевдокод алгоритма с учетом того, что на
вход подается отсортированное по координате x множество точек.
Предварительная сортировка элементов множества S по координате x требует O(n log n) операций. Благодаря сортировке и разделению на шагах 5-6 алгоритма, множества CH (P) и СЯ(Р2) представляют два непересекающихся трехмерных выпуклых многогранника. Основная сложность заключается в выполнении функции Merge(A,B) за время O(|A| + |B|).На этом шаге необходимо построить цилиндрическую триангуляцию (см. рис. 12), опирающуюся на выпуклые оболочки A и B по некоторым контурам, и удалить из A и B части, оказавшиеся скрытыми. К выпуклой оболочке CH( A и B) добавится некоторый «обод» из граней с топологией цилиндра без оснований. Количество граней линейно зависит от размера двух многогранников: каждая грань использует как минимум одно ребро из А или из В. Таким образом, количество граней не превосходит общего количества ребер.
Рассмотрим шаг слияния двух оболочек более подробно: сначала будет описана процедура слияния двух выпуклых оболочек из [6], а затем будут предложены улучшения.
Рассмотрим слияние выпуклых оболочек.
В [6] для представления структуры выпуклой оболочки используется реберный список с двойными связями (РСДС). Укрупнено шаг слияния можно представить следующим образом:
1. Построить цилиндрическую триангуляцию Г, опирающуюся на А и В.
2. Удалить из А и В части, оказавшиеся скрытыми в результате построения триангуляции Г.
Основываясь на интуитивных соображениях, построение триангуляции Г можно рассматривать как операцию заворачивания одновременно двух «подарков» в один сверток. Хотя Г может иметь o (п) граней, а каждый шаг заворачивания в общем случае требует o (п) операций. Использование особенностей трехмерных многогранников позволяет решить эту задачу за линейное время.
Построение триангуляции начинается с нахождения некоторого ее ребра. Наиболее удобный способ состоит в проектировании выпуклых оболочек А и В на плоскость ХОТ и затем нахождении нижнего мостика е (таким образом можно поддерживать нижнюю выпуклую оболочку проекции точек на ХОТ) (см. рис. 13). Имея ребро е, можно начать построение Г, выбрав в качестве опорной плоскость, проходящую через ребро е параллельно оси
На очередном шаге построения Г в качестве базы используется последняя из уже
Рис. 12. Слияние непересекающихся выпуклых оболочек: я) выпуклые многогранники до соединения; б) результирующая выпуклая оболочка. Жирные ребра показывают границу новых граней
Лис 1 2 3 4 5 6 7 8 9 тинг 2. Algorithm DIVIDE-AND-CONQUER(P) ® CH(P) if ( P £ k0 ) then построить CH (P) любым методом вернуть построенную CH ( P) end-if P — {Pl,•••, pyn/2j}; P2 — ÎPL»/2 j+1 >->Pn } A — CH (P); B — CH ( P2); H — Merge ( A, B); вернуть H в качестве построенной CH (P)
построенных граней триангуляции Г. Пусть грань (а2, Ь2, a1) является базовой на текущем шаге. Далее среди вершин, смежных с а2 необходимо выбрать вершину а так, чтобы грань (а2,Ь2,а) образовала наибольший выпуклый угол с (а2,Ь2,а1) среди всех граней (а2,Ь2,V) для вершин V, смежных с а2 и V Ф а1.
Аналогичным образом среди всех вершин, смежных с Ь2 выберем вершину Ь. Теперь, когда выявлены два претендента (а2, Ь2, а) и (а2, Ь2, Ь), делается заключительное сравнение: если (а2,Ь2,а) образует с (а2,Ь2,а1) больший выпуклый угол, чем
(а2, Ь2, Ь) , то а добавляется к Г (в противном случае добавляется Ь ), и на этом шаг построения очередной треугольной грани заканчивается. Используя тот факт, что в РСДС можно эффективно проходить ребра, инцидентные некоторой вершине в порядке обхода по часовой стрелке или против нее, можно представлять информацию о пройденных вершинах для выпуклых оболочек следующим образом (см. рис. 14).
Пусть грань (, Ь, а) образует наибольший выпуклый угол с (Ь1, Ь, а) из всех (Ьi,Ь,а) с г = 2, ..., к. Любое ребро (Ьi,Ь) при 1 < г < $ оказывается внутри выпуклой оболочки СН (Р1 и Р2), и поэтому его можно исключить из дальнейшего рассмотрения. Просмотр ребер, инцидентных я, сле-
дует начинать с последнего из просмотренных ребер, так как все другие удалены. Таким образом, на заворачивание требуется О(ребервСИ(Р1 иР2)) операций.
Удаление из А и В частей, оказавшихся скрытыми, можно сделать за линейное время от размера удаляемых частей (процесс удаления можно охарактеризовать как обход структуры, начиная с заведомо удаляемой грани, и не переходящий через ребра, помеченные как принадлежащие «ободам» цилиндрической триангуляции).
Таким образом, слияние СН(Р1) и СН(Р2) может быть выполнено за время О((р иР2)\).
Как показывает опыт реализации алгоритма, построение цилиндрической триангуляции можно упростить, если хранить неполную информацию из РСДС: можно исключить информацию о ребрах выпуклой оболочки. Для построения цилиндрической триангуляции достаточно информации о структуре многогранников, состоящей из ссылок между гранями (каждая грань имеет три ссылки на грани смежные по ребрам, перечисленные в порядке обхода по часовой стрелке).
Таким образом, вместо того, чтобы обходить ребра, инцидентные вершине в порядке обхода против часовой стрелки, достаточно обойти грани сливаемых выпуклых оболочек, имеющие общие ребра с ободом цилиндрической триангуляции, и которые будут скрыты после слияния (то есть внут-
Рис. 13. Начальный и последующие шаги процедуры построения
Рис. 14. Шаг, обеспечивающий продвижение при построении триангуляции Г = 4).
ренние грани). Обход граней производится по ободу цилиндрической триангуляции (см. рис. 15).
Пройденных граней будет не более чем общее количество граней в сливаемых оболочках, и, следовательно, общая сложность слияния есть О (п).
Заметим, что триангуляция Г не может быть однозначно идентифицирована в тех случаях, когда грани цилиндрической триангуляции компланарны с гранями сливаемых выпуклых оболочек (например, при распределении точек на кубе этот случай встречается довольно часто). Это может создать проблемы с построением Г [11] (цилиндрическая триангуляция не всегда строится од-носвязной). На рис. 16 проиллюстрирована эта ситуация.
Реализация [11] использует внешний обод, однако удобнее строить цилиндрическую триангуляцию для внутреннего обода и, таким образом, упростить обработку частных случаев. Для выявления кандидата на включение в триангуляцию на следующем шаге, в случае компланарности граней-кандидатов, нужно брать ту грань, которая полностью принадлежит другой грани, если же ни одна не входит в другую, то можно брать произвольную.
После построения цилиндрической триангуляции можно обновить структуру РСДС
за линейное время. Заметим, что при таком подходе к построению цилиндрической триангуляции существенно упрощается удаление скрытых частей сливаемых оболочек.
Размер необходимой алгоритму памяти определяется тем, что при хранении сливаемых выпуклых оболочек и цилиндрической триангуляции требуется хранить примерно 4п граней в худшем случае, что превосходит в два раза затраты на хранение просто граней выпуклой оболочки.
6. БЫСТРЫЙ алгоритм
На содержательном уровне этот метод последовательно обрабатывает по одной точке, назовем ее р е Р, и если р - внешняя точка по отношению к текущей выпуклой оболочке СН (Р), то из точки р строится опорный конус к текущей СН (Р) и
Внешний
Внутренний обод
Рис. 15. Построение цилиндрической триангуляции при обходе граней, смежных с ободом цилиндрической триангуляции
Промежуточный обод
Рис. 16. Неоднозначность построения цилиндрической триангуляции Г. Грани, помеченные одним цветом, лежат в одной плоскости. Триангуляция Г может опираться как на внешний или внутренний обод, так и на любой из «промежуточных» ободов.
информатика
13
Рис. 17. Добавление точки рг. Горизонт многогранника
удаляется часть оболочки СН (Р), затеняемая этим конусом.
Обозначим за Рг множество {р15..., рг}, где г > 1. Рассмотрим шаг алгоритма добавления точки рг к уже построенной выпуклой оболочке Рг _1. Иными словами, рассмот-
рим переход от СН (Рг _1) к СН (Рг). Возможны два случая:
- точка рг лежит внутри СН (Рг_1), либо на её границе, тогда СН (Рг) = СН (Рг _1).
- точка рг лежит вне СН (Рг_1). Предположим, что в точке рг находится точечный источник света (см. рис. 17). Некоторые грании СН (Рг _1) будут «освещены», а остальные грани будут «в тени». Освещенные или видимые грани формируют связанную область на поверхности выпуклой оболочки СН(Рг_1), называемую видимым регионом точки рг на СН(Рг_1). Видимый регион ограничен замкнутой кривой, состоящей из ребер СН (Рг _1), в дальнейшем называемой горизонтом точки рг на СН (Рг_1). Проекция горизонта является границей выпуклого многоугольника, полученного проецированием СН (Рг _1) на плоскость с центром проекции в точке рг .
Листинг 3. Algorithm QUICKHULL(P) ® CH(P)
Вход. Исходное множество точек
Выход. Выпуклая оболочка множества точек СН(Р), заданная набором треугольных
граней.
1 найти четыре точки px, p2, p3, p4 из множества P, образующие
тетраэдр с ненулевым объемом
2 С — СН ( Р2, Рз, Р4})
3 for каждой грани F из СН ({, p2, p3, p4})
4 for каждой не отнесенной точки p
5 if p находится над F then
6 отнести p ко внешнему множеству F
7 for каждой грани F из C с непустым внешним множеством
8 выбрать самую дальнюю точку p из внешнего множества F
9 инициализировать видимое множество V в F
10 for каждой непомеченной грани N, являющийся соседом грани из F
11 if p находится над N then
12 добавить N в V
13 преобразовать границу V в горизонт H
14 for каждого ребра R из H
15 создать новую грань из R и p
16 проставить ссылки новой грани на её соседей
17 for каждой новой грани F'
18 for каждой не отнесенной точки q из внешнего множества грани из V
19 if q находится над F' then
20 отнести q ко внешнему множеству F'
21 удалить грани из V
22 вернуть C
Горизонт точки рг играет важную роль в преобразовании СН(Рг_1) в СН(Рг): он представляет собой границу между частью поверхности, которая должна быть сохранена (невидимые грани), и частью поверхности, которая должна быть удалена (видимые грани).
Видимые грани должны быть замещены гранями, образованными точкой рг и её горизонтом.
Центральной задачей алгоритма является эффективное определение опорного конуса для добавляемой точки р. Заметим, что поскольку каждая грань имеет ссылки на ее соседей, нахождение первой видимой грани из точки рг позволяет быстро найти оставшиеся видимые грани (за линейное время от количества этих граней).
В быстром алгоритме для нахождения первой видимой грани используются внешние .множества точек для каждой грани. Точка находится во внешнем множестве грани, если она находится над гранью, каждая точка находится только в одном внешнем множестве. Отличительной особенностью алгоритма (см. листинг 3) является добавление самой дальней точки из внешнего множества грани.
В [12] приводится эмпирический анализ алгоритма. Определяются условия балансировки, при которых гарантируется хорошее поведение алгоритма в случаях, когда все точки исходного множества входят в выпуклую оболочку. При выполнении условий балансировки алгоритм QUICKHULL в среднем работает за время O(n log n).
Приложение
РЕБЕРНЫЙ СПИСОК С ДВОЙНЫМИ СВЯЗЯМИ
Имея в виду эффективность реализации действий, типовых для многих алгоритмов, удобно представлять плоский прямолинейный граф с помощью реберного списка с двойными связями (РСДС) [6].
Пусть задан плоский граф О = (V, Е), где V = } - вершины и Е = {е1 } - ребра.
Главный элемент РСДС для плоского графа О — это реберньш узел. Между ребрами графа и реберными узлами РСДС существует взаимно однозначное соответствие, то есть каждое ребро представлено в РСДС ровно один раз. Реберный узел РСДС (см. рис. 18), соответствующий ребру графа, например, ек = {г1 ,г2}, имеет четыре информационных поля 2, Л, Р 2) и два поля указателей (Р1, Р2). Значения этих полей таковы. Поле VI содержит начало ребра, а поле V 2 содержит его конец (так изначально неориентированное ребро получает условную ориентацию, которая не несет пока никакой смысловой нагрузки). Поля и Р2 содержат имена граней, лежащих слева и справа от ориентированного ребра (г1, г2). Указатель Р1 (соответственно Р2) задает реберный узел, содержащий первое ребро, встречаемое вслед за ребром (г1, г2), при повороте от него против часовой стрелки вокруг г1 (соответственно г2).
Например, для графа, изображенного на рис. 19, реберный список и массивы входов в него по вершинам и граням приведены на рис. 20.
ребро V1 V2 F1 F2 P1 P2
ek Vl V2 Л f2 P1 P2
Рис. 18. Представление плоского графа с помощью РСДС
ei
Рис. 19. Плоский граф, ребрам которого придана произвольная ориентация
а) ребра V1 V2 F1 F2 P1 P2
e1 1 2 1
4
5 2
e2 2 4 1 4 1 7
е3 1
3
4 2 1 4
б) V
head_V
v1 1
v2 2
v3 4
v4 7
v5 6
*) F
head_F
f1 1
f2 2
f3 4
f4 7
С помощью РСДС можно легко вычислить ребра, инцидентные заданной вершине следующим образом (см. листинг 4).
Время работы этой процедуры пропорционально числу ребер, инцидентных вершине V.
Обход заданной грани выполняется с помощью аналогичной процедуры (см. листинг 5).
В некоторых случаях полезно использовать более универсальный вариант РСДС, но требующий несколько большей памяти. Назовем его РСДС с удвоением ребер. В этом случае каждое неориентированное ребро исходного графа представляется парой ориентированных ребер (иолуребер). Такое представление поясняется на рис. 21. Именно оно, как правило, используется в описанных алгоритмах построения трехмерных выпуклых оболочек.
Листинг 4
Proc Инцидентные_ребра (v: Вершина; var A: Вектор_ребер); {A[1..*] - список ребер, инцидентных v, в порядке против часовой стрелки}
{a - текущее ребро, Список - РСДС }
var a, a0: ребро; i: индекс;
begin
a := Head_V[v]; a0 := a; i := 0;
{inv: ((CnucoK[a].v1 = v) or (Список [a].v2 = v)) & A[1..i] - заполнен} repeat
i := i + 1; A[i] := a;
if Список^]у1 = v then a := Список^]^
else a := Списоко]^;
until a = a0;
end { Инцидентны1е_ребра }
e4
3
4
3 2 6
5
e5 1
4 2 1 3 2
Листинг 5
Proc Граница_грани (f: Грань; var A: Вектор_ребер);
{A[1..*] - список ребер границы грани в порядке по часовой стрелке}
{ a - текущее ребро, Список - РСДС }
var a, a0: ребро; i: индекс;
begin
a := Head_F[v]; a0 := a; i := 0;
{inv: ((Список^.А = f) or (Список [a].f2 = f)) & A[1..i] - заполнен} repeat
i := i + 1; A[i] := a;
if Список[a].f1 = f then a := СписокМ^ else a := СписокМ^;
until a = a0;
end { Граница_грани }
Рис. 20.
(а) РСДС,
(б) входы по вершинам Нвай_У [1..п] и
(в) входы по граням Нва1Л_Р[1..Г] для графа на рис. 19.
я)
V2
e
f e1 e
e
f2
б)
Полуребро Вершина V Парное ребро Е Грань слева F Следующее ребро P1 Предыдущее ребро P2
ei vi e2 fi e" e'
e2 V2 ei f2 * e ** e
Рис. 21. Представление плоского графа (я) с помощью РСДС с удвоением ребер (б).
Литература
1. Нвяновский С.А., Лреобряженский А.С., Силончик С-К Алгоритмы вычислительной геометрии. Выпуклые оболочки: простые алгоритмы // Компьютерные инструменты в образовании, 2007, №1. С. 4-19.
2. Нвяновский С.А., Лреобряженский А. С., Силончик CK Алгоритмы вычислительной геометрии. Выпуклые оболочки: связь с задачей сортировки и оптимальные алгоритмы // Компьютерные инструменты в образовании, 2007, №2. С. 6-18.
3. Гильберт Д., ^он-Фоссен С. Наглядная геометрия, 1981. 344 с.
4. Берже М. Геометрия: в 2 т. Т. 1. М.: Мир, 1984. 560 с.
5. Лекции по теории графов /Елеличев ВА. и др. М.: Наука. Гл.ред.физ.-мат.лит., 1990. 384 с.
6. Яреиярямя Ф., Шейлос М. Вычислительная геометрия: Введение. М.: Мир, 1989. 478 с.
7. D.R. Chand and S.S. Кяриг. An algorithm for convex polytopes, J. ACM, 17:78-86, 1970.
8. В.А. Яльин, Э.Г. Яознлк. Аналитическая геометрия. М.: ФИЗМАТЛИТ, 2002. 240 с.
9. ^орлен Г., Лейзерсон Ривест Р. Алгоритмы: построение и анализ. М.: МЦМНО, 2000. 960 c.
10. F.P. Preparata and S.J. Hong. Convex hulls of finite sets of points in two and three dimensions. Commun. ACM, 20:87-93, 1977.
11. A.M. Day. The implementation of an algorithm to find the convex hull of a set of three-dimensional points. ACM Trans. on Graphics, 9:105-132, 1990.
12. B. Barber, D. Dobk/n, and H. Hwhdanpaa. The quickhull algorithmfor convex hull. Technical Report GCG53, The Geometry Center, MN, 1993.
© Наши авторы, 2007 Our authors, 2007
Ивановский Сергей Алексеевич, кандидат технических наук, доцент кафедры Математического обеспечения и применения ЭВМ СПбГЭТУ «ЛЭТИ»,
Преображенский Алексей Семенович, аспирант СПбГЭТУ «ЛЭТИ», магистр прикладной математики и информатики,
Симончик Сергей Константинович, аспирант СПбГЭТУ «ЛЭТИ» магистр прикладной математики и информатики.