Конспект по ОБЖ: "Как избежать попадания в экстремальную ситуацию"(6 класс). Триангуляция монотонных полигонов

Полигон относится к типу объектов с замкнутыми контурами. В полигонах хранится информация об их внутренних и внешних контурах и о других полигонах, вложенных в эти полигоны или сгруппированные с ними.

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

Внутри полигонов могут находиться другие полигоны.

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

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

Термин Определение
Границы Замкнутые границы, образующие полигон. Возможно наличие в полигонах множества непересекающихся контуров либо контуров, вложенных в другие контуры.
Обеспечение баланса Процесс перерасчета для определения того, какие из контуров являются внешними и какие внутренними. Вложенные контура попеременно классифицируются в качестве внешних и внутренних контуров. То есть, наружный контур считается внешним контуром. Контур, вложенный вовнутрь данного контура, считается внутренним контуром. Контур, вложенный во внутренний контур, считается внешним контуром.
Внутренний контур Вложенный контур, полностью находящийся внутри внешнего контура.
Внешний контур Наружный контур для любой дискретной последовательности контуров, определяющих полигон, или контур, находящийся внутри внутреннего контура. Полигон может иметь несколько невложенных наружных контуров и несколько вложенных внешних контуров.

Понятие о контурах

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

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

В дополнение к внешним и внутренним контурам имеется контур типа "Аннотация ". Данный контур совпадает по характеристикам с внутренним контуром, однако влияет только на отображение образца заполнения и игнорируется при расчете площади или интерьера полигонального объекта. Его первичное назначение состоит в предоставлении пользователю возможности аннотировать имеющиеся чертежи независимо от затемнения аннотаций образцом заполнения полигона. Аннотация состоит обычно из текста или блоков.

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


В форме свойств полигона пока­заны: его собственный номер, площадь, собственный условный знак, которым он будет изображаться в режиме Установки.
Ниже показана изменяемая ин­формация: групповая принадлежность, собственные символ и цвет, признак выбора (галочка). Если полигон относится к группе, которая не имеет базы данных в блоке, групповую при­надлежность полигона можно менять свободно. Если база данных для этой группы определена, полигон можно только Удалить и создать снова, уже другой группы.
Примечание. Если полигон отно­сится к группе с БД, изменить его группы можно с некоторыми ограничениями, используя отдельную специальную операцию изменения группы (см. 2.6.2.2)
Кнопка Указать позволяет ука­зать новое положение точки внутри полигона (выдела), на котором будет показана его метка (номер выдела или формула). Предварительную расстановку меток при создании полигонов TopoL выполняет автоматически, однако их положение для полигонов сложной формы может не быть удачным. Подправить придется вручную.
Кнопка OK подтверждает выполненную операцию, Cancel - отменяет. Кнопка Выход завершает операцию редактирования полигонов.


Кнопка БД атри­бутов вызыва­ет форму редактора текущей записи БД при полигонах, по­ка­зан­ную на рис. слева. Для примера здесь показанна запись"длин­ного" фор­мата (см. 6.3.3) полиго­нов группы 7550 - выделов, исполь­зуемого в ин­форма­ци­он­ной сис­те­ме TopoL_L.
Серые строчки в таблице - систем­ные поля БД блока, которые TopoL зано­сит и изменяет сам, редактировать их невозможно.
При создании новых полигонов группы, для которой определена БД блока, форма редактора записи БД полиго­нов вызывается автоматически сразу после создания полигона.
Кнопка Browse / Просмотреть поз­во­ляет увидеть сразу всю таблицу внутренней БД блока для выделов. Для просмотра БД в TopoL используется специальная форма (см. 2.6.6).
Кнопка Join / Связать позволяет увидеть через модель данных блока TopoL запись или записи присоединенной внешней базы данных, как показано на следующем рисунке. Для случая базы данных повыдельного блока через модель видна запись таксационной базы для того же выдела. Именно такой механизм доступа используется для тематической раскраски карт на основе данных таксации выделов.
Последняя кнопка Делить позволяет разделить полигон на две части с контролем площадей при делении. Эта операция может оказаться очень полезной, если требуется, чтобы площадь какого-то полигона точно соответствовала некоторой величине (например, чтобы площадь выдела культур на карте соответствовала документам). При ее нажатии появляется форма, показан­ная на рисунке слева.
Выполнить деление можно, либо автоматически, либо настраивая положение линии деления вручную, что дает бóльшую гибкость настрой­ки.
Автоматически деление выпол­няется только смещением заданной линии деления. Для автоматического деления нужно сначала настроить площади. В верхней части формы надо указать, какой из полигонов будет иметь Приоритет по площади, а под ним либо в верхней строке Область указать ожидаемую площадь правого полигона, либо под ним - левого полиго­на. Вы задаете желаемую площадь приоритетного полигона и щелкаете мышкой в поле площади второго - программа автоматически относит туда всю остальную площадь.
Затем надо задать направление линии деления. Направление задается либо явным указанием Угла, либо указанием мышкой в окне карты направления линии деления. Для указания мышкой надо в режиме Передвинуть нажать кнопку Указать параметры. Далее заданием двух точек задается направление линии деления. Для возврата в форму деления используется правая кнопка мышки.
После настройки параметров надо нажать кнопку Запуск. Полигон будет разделен в соответствии с заданными параметрами. Если результат автоматического деления Вам не понравился, отмените эту операцию (см. 1.12.2).
Для настройки и выполнения деления вручную площади задавать не нужно. Просто в нижней части формы настраиваются правила деления. Перераспре­делять площади между правым и левым полигонами можно либо смещением границы деления - Передвинуть, либо ее поворотом - Вращать. Для указания начальных параметров деления используется кнопка Указать параметры. В режиме смещения можно, указав мышкой две точки в окне карты, задать направление линии деления. В режиме поворота указывается одна точка, относительно которой вращается линия деления. Кнопка OK активизирует операцию деления, но деление можно начать и сразу после задания начальных параметров.
Для деления полигона используются стрелочные клавиши на клавиатуре. Нажатие на левую стрелку смещает или поворачивает линию деления влево, на правую - соответственно, вправо. В информационной строке ниже окна карты программа показывает площади левой и правой частей полигона. Стрелка вверх увеличивает шаг перемещения линии деления, стрелка вниз - уменьшает. Используя эти инструменты и постепенно уменьшая шаг смещение можно подобрать требуемые значения площадей.
Практический совет. Если требуется, чтобы площадь полигона инстру­мен­тального выдела точно соответствовала учетному значению, используйте операцию деления полигона. Можно в режиме деления построить последнюю замыкающую линию полигона между инструментальным и соседним полигонами, либо, если полигон уже существует и литерован, отрезать делением от него лишнюю площадь, затем этот осколок слиянием полигонов (см. ниже) присоединить к соседнему выделу. Если у исходного выдела площади не хватает, увеличить ее можно смещением узлов (см. 2.6.3.36).

2.6.2.2 Изменить код группы

Меню при кнопке позволяет вызывать операции с полигонами. Операция Изменить код группы позволяет переводить объекты из группы в группу. Перевести в заданную группу можно как все объекты одной или нескольких исходных групп, так и только выбранные объекты этих групп. Вид формы см. в разделе 2.6.3.14 . Если объекты исходной и целевой групп не имеют базы данных, этой операцией можно пользоваться свободно. Если объекты имеют базу данных одинакового формата, либо в целевой группе нет пока ни одного объекта, проблем также не будет - все атрибуты базы данных будут перемещены в целевую группу вместе с объектами. Но если форматы БД исходной группы и целевой различаются, при переводе потери данных БД избежать не удастся.

2.6.2.3 Сохранить атрибуты

Операция Сохранить атрибуты используется для сохранения текущей настройки визуализации полигонов в качестве их собственных символов и цветов. После выполнения этой операции TopoL при задании визуализации в режиме Установка именно так будет показывать карту. То есть, установив визуализацию "План лесонасаждений" и сохранив атрибуты, Вы все время будете видеть план лесонасаждений, пока не измените вид карты и не сохраните этот другой вид.

2.6.2.4 Удалить выбранные

Операция Удалить выбранные просто удаляет все выбранные полигоны и связанные с ними записи БД блока. Линии границ полигонов остаются.

2.6.2.5 Слить указанные курсором


Операция Слить указанные курсором является расширением стандартного набора операций TopoL (см. рисунок слева). Она позволяет присоединить в указан­ному первым полигону ядра (выделен диагональной штриховкой) другие выбранные мышкой полигоны (показаны цветом выборки, здесь - красным). Резуль­тирующий слитый полигон имеет такие же атрибуты и запись базы данных, какие были у полигона ядра, но, соответственно, бóльшую площадь.
Эта операция похожа на операцию слияния выде­лов из линейки Таксация (см. 1.12.12), но в отличие от нее ничего не знает о содер­жании баз данных.

2.6.2.6 Создать все видимые

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

2.6.2.7 Создать по заданным точкам

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

2.6.2.8 Корректировать топологию

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

Теперь рассмотрим проблему вычисления области пересечения двух выпуклых полигонов P и Q . За исключением особо оговоренных случаев будем предполагать, что два полигона пересекаются невырожденно : пересечение двух ребер происходит в одной единственной точке, не являющейся вершиной какого-либо полигона. Учитывая такое предположение о невырожденности , всегда получим, что полигон состоит из попеременных цепочек из Р и Q . Каждая пара последовательных цепочек соединяется в точке пересечения, в которой пересекаются границы полигоновP и Q (рис. 6.11).

Существует несколько решений этой задачи с линейной зависимостью времени выполнения от суммарного числа вершин. Описываемый здесь алгоритм обладает особым изяществом и его легко применять. Для двух заданных на входе выпуклых полигонов P и Q алгоритм определяет окно на ребре полигона P ещё одно окно на ребре полигона Q . Идея заключается

Рис. 6.11. Структура полигона пересечения .

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

Для объяснения работы оказывается весьма полезным ввести понятие серпа. На рис. 6.12 серпами будут шесть затененных полигонов. Каждый из них ограничен цепочкой, взятой от полигона P , и цепочкой от полигона Q , ограниченных двумя последовательными точками пересечения. Внутренней цепочкой серпа будет та часть, которая принадлежит полигону пересечения. Отметим, что полигон пересечения окружен четным числом серпов, внутренние цепочки которых попеременно являются частями границ полигонов P и Q .

Рис. 6.12. Серпы, окружающие полигон пересечения.

В терминах серпов алгоритм поиска полигона пересечения проходит две фазы. В процессе первой фазы окно p полигона P и окно q полигона Q перемещаются в направлении по движению часовой стрелки до тех пор, пока они не будут установлены на ребрах, принадлежащих одновременно одному и тому же серпу. Каждое окно начинает свое движение с произвольной позиции. Здесь для краткости будем использовать один и тот же символ p для обозначения как окна полигона P , так и ребра в этом окне. Тогда термин "начало p " будет относиться к точке начала ребра в окне полигона P , а команда "продвинуть p " будет означать, что необходимо переместить окно полигона P на следующее ребро. Аналогичным образом буквой q будем обозначать как окно полигона Q , так и ребро в окне. Иногда ребра p и q будем считать текущими ребрами.

Во время фазы 2 p и q продолжают перемещаться в направлении по часовой стрелке, но на этот раз они двигаются в унисон от одного серпа к соседнему серпу. Перед тем как любое окно перейдет из текущего серпа в соседний , ребра p и q пересекутся в точке пересечения, соединяющей оба серпа. Полигон пересечения строится именно во время второй фазы. Перед каждым перемещением p конечная точка ребра p заносится в полигон пересечения, если ребро p принадлежит внутренней цепочке текущего серпа. Аналогичным образом перед перемещением q фиксируется конечная точка ребра q , если q принадлежит внутренней цепочке текущего серпа. При каждом пересечении ребер p и q точка пересечения, в которой они пересекаются, записывается в полигон пересечения.

Для принятия решения, какое из окон должно перемещаться, в алгоритме используется правило перемещения. Это правило основано на следующих замечаниях: говорят, что ребро a нацелено на ребро b , если бесконечная прямая линия, определяемая ребром b , расположена перед а (рис. 6.13).

Рис. 6.13. Только показанные толстыми линиями ребра нацелены на ребро q , остальные - нет.

Ребро a нацелено на b , если выполняется одно из условий:

Заметим, что соотношение соответствует случаю, при котором угол между векторами a и b меньше 180 градусов.

Функция aimsAt возвращает значение TRUE , если и только если ребро a нацелено на ребро b . Параметр aclass указывает на положение конечной точки а.dest относительно ребра b .

Параметр crossType принимает значение COLLINEAR , если и только если ребра a и b коллинеарные.

bool aimsAt (Edge & а , Edge &b, int aclass , int crossType )
{Point2 va = a.dest a.org;
Point2 vb = b.dest b.org;
if (crossType != COLLINEAR)
{if((va.x * vb.y ) >= (vb.x * va.y ))
return (aclass !=
RIGHT);
else
return (aclass != LEFT);
}
else
{return (aclass != BEYOND);
}
}

Если ребра a и b коллинеарны , то ребро a нацелено на b , если конечная точка a.dest не лежит после b . Это обстоятельство используется для того, чтобы продвинуть a вместо b , когда два ребра пересекаются вырожденно более, чем в одной точке. Позволяя a "догонять" b , мы обеспечиваем, что ни одна точка пересечения не будет пропущена.

Возвратимся к обсуждению правил перемещения. Они сформулированы таким образом, чтобы не пропустить следующую точку пересечения. Правила отличают текущее ребро, которое может содержать следующую точку пересечения, от текущего ребра, которое возможно не может содержать следующей точки пересечения, причем в этом случае окно переносится вполне безопасно. Правила перемещения различают четыре ситуации, показанные на рис. 6.14. Здесь ребро a считается находящимся вне ребра b , если конечная точка a.dest расположена слева от b .

Рис. 6.14. Четыре правила перемещения: (а) - продвинуть p ; (б) - продвинуть p ; (в) - продвинуть p , (г) - продвинуть р.

1. p и q нацелены друг на друга: перемещается окно, соответствующее тому ребру (p или q ), которое находится снаружи другого. В ситуации рис. 6.14 а должно быть перенесено окно на ребре р . Следующая точка пересечения не может лежать на p , поскольку ребро p находится вне полигона пересечения.

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

3. q нацелено на p , но p не нацелено на q : конечная концевая точка ребра q заносится в полигон пересечения, если q не находится снаружи от p , после чего переносится окно q (рис. 6.14в). Этот случай симметричен предыдущему. На рис. показана ситуация, при которой ребро q , окно которого должно быть перенесено, находится снаружи от ребра p .

4. p и q не нацелены друг на друга: переносится то окно, которое относится к ребру, расположенному снаружи от другого. Согласно рис. 6.14 необходимо перенести окно p , поскольку ребро p находится снаружи от ребра q .

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

Рис. 6.15. Поиск полигона пересечения. Для ребра указана метка i , если оно обрабатывается на шаге i . Два начальных ребра обозначены меткой 0.

метку 0 . На этом рисунке фаза 2 (когда два текущих ребра оказываются принадлежащими одному и тому же серпу) начинается после трех итераций. Алгоритм реализован в программе convexPolygonIntersect . Программе передаются полигоны P и Q , она возвращает указатель на результирующий полигон пересечения R . Обращение к функции advance использовано для переноса одного из двух текущих ребер и для включения конечной концевой точки ребра в полигон R в соответствии с выполнением определенных условий. Используются окна, существующие внутри класса Polygon .

enum (UNKNOWN, P_IS_INSIDE, Q_IS_INSIDE) ;
Polygon *convexPolygonIntersect (Polygon &P, Polygon &Q)
{Polygon *R;
Point iPnt , startPnt ;
int inflag = UNKNOWN;
int phase = 1;
int maxItns = 2 * (P.size О + Q.size О);
// начало цикла for
for (int i = 1; (i <=maxItns ) || (phase==2); i++ )
{Edge p = P.edge ();
Edge q = Q.edge ();
int pclass = p.dest.classify (q );
int qclass = q.dest.classify (p );
int crossType = crossingPoint (p , q , iPnt );
if (crossType == SKEW_CROSS)
{ if (phase == 1)
{phase = 2;
R = new Polygon ;
R->insert (iPnt );
startPnt = iPnt ;
}
else
if (iPnt !=
R->point())
{if (iPnt != startPnt )
R->insert(iPnt );
else
return R;
}
if (pclass ==RIGHT)
inflag = P_IS_INSIDE;
else
if(qclass ==RIGHT)
inflag = Q_IS_INSIDE;
else inflag = UNKNOWN;
}
else
if((crossType ==COLLINEAR) &&
(pclass != BEHIND) && (qclass != BEHIND))
inflag = UNKNOWN;
bool pAIMSq = aimsAt (p, q, pclass , crossType );
bool qAIMSp = aimsAt (q, p, qclass , crossType );
if (pAIMSq && qAIMSp )
{if ((inf lag == Q_IS__INSIDE)||
((inflag == UNKNOWN)&&(pclass ==LEFT)))
advance(P, *R, FALSE);
else
advance(Q, *R, FALSE);
}
else
if(pAIMSq )
{advance(P, *R, inflag == P_IS_INSIDE);
}
else
if (qAIMSp )
{advance(Q, *R, inflag == Q_IS_INSIDE);
}
else
{if ((inflag == Q_IS_INSIDE)
((inflag == UNKNOWN) && (pclass == LEFT)))
advance(P, *R, FALSE);
else
advance(Q, *R, FALSE);
}
}
//
конец
цикла for
if (pointInConvexPolygon (P.point (), Q))
return new Polygon(P);
else
if (pointlnConvexPolygon (Q.point (), P))
return new Polygon(Q);
return new Polygon;
}

Если после выполнения итераций не будет обнаружено ни одной точки пересечения, то основной цикл завершается, поскольку это означает, что границы полигона не пересекаются. Последующие обращения к функции pointInConvexPolygon производятся с целью обнаружения ситуаций , или =0. В противном случае, если найдена некоторая точка пересечения iPnt , то алгоритм продолжает построение полигона пересечения R и останавливается только после того, как точка iPnt будет обнаружена вторично.

Переменная inflag показывает, какой из двух входных полигонов находится в данный момент внутри другого - т. е. указывает на полигон, текущее ребро которого находится в составе внутренней цепочки текущего серпа. Более того, переменная inflag принимает значение UNKNOWN (неизвестно) во время первой фазы, а также всякий раз, когда оба текущих ребра коллинеарны или перекрывают друг друга. Значение этой переменной меняется при каждом обнаружении новой точки пересечения.

Процедура advance продвигает текущее ребро полигона A , представляющее либо P , либо Q . Эта же процедура заносит конечную концевую точку ребра x в полигон пересечения R , если A находится внутри другого полигона и x не была последней точкой, записанной в R :

void advance(Polygon2 &A, Polygon2 &R, int inside)
{A.advance (CLOCKWISE);
if(inside && (R.point () != A.point ()))
R. insert (A.point ()) ;
}

Анализ и корректность.

Доказательство корректности показывает наиболее важные моменты работы алгоритма - один и тот же набор правил продвижения работает в течение обеих фаз. Правило продвижения заносит p и q в один и тот же серп и затем передвигает p и q в унисон из одного серпа в другой.

Корректность алгоритма следует из двух утверждений:

Утверждение 2 обеспечивает, что алгоритм найдет некоторую точку пересечения, если таковая существует. Поскольку ребра p и q принадлежат одному и тому же серпу, если они пересекаются, то из утверждения 1 следует, что остальные точки пересечения будут найдены в нужном порядке.

Рассмотрим сначала утверждение 1. Предположим, что p и q принадлежат одному и тому же серпу и q достигает некоторой точки пересечения раньше, чем р . Мы покажем, что тогда q останется неподвижным, пока p не достигнет точки пересечения после ряда последовательных продвижений. Могут возникнуть две ситуации. Сначала предположим, что p находится снаружи от q (рис. 6.16а). При этом q останется фиксированным, пока p будет продвигаться согласно нулю или нескольким применениям правила 4, затем нулю или нескольким применениям правила 1 и потом нулю или нескольким применениям правила 2. Во второй ситуации предположим, что p не находится снаружи от q (рис. 6.16б). Здесь q будет оставаться фиксированным, пока p будет продвигаться путем нуля или нескольких применений правила 2. В симметричной ситуации, когда p достигает точки пересечения раньше, чем q , ребро q остается фиксированным, а ребро q продвигается до точки встречи. Это можно показать аналогичным образом, только меняется роль p и q и правило 3 заменяет правило 2. Из этого следует утверждение 1.

Чтобы показать утверждение 2, предположим, что границы P и Q пересекаются. После итераций либо p , либо q должны совершить полный оборот вокруг своего полигона. Предположим, что это произошло с р . В некоторый момент времени ребро p должно расположиться так, что оно будет содержать точку пересечения, в которой полигон Q переходит извне полигона P внутрь его. Это происходит потому, что существуют по крайней мере две точки пересечения и направление пересечения меняется. Пусть q будет ребром внутри окна полигона Q в момент обнаружения такого р .

На рис. 6.17 граница полигона Q разбита на две цепочки и . Первая цепочка заканчивается в ребре , в том ребре полигона Q , которое входит внутрь полигона P через его ребро p . Другая цепочка заканчивается

Рис. 6.16. Продвижение к следующей точке пересечения.

в ребре , конечная точка которого лежит справа от бесконечной прямой линии, определяемой ребром p , и она наиболее удалена от этой прямой линии. Следует рассмотреть два случая в зависимости от того, какой из двух цепочек принадлежит ребро q :

Случай 1 . Здесь p остается фиксированным, тогда как q продвигается согласно нулю или нескольким применениям правила 3, затем правила 4, потом правила 1 и, наконец, правила 3, когда обнаруживается точка пересечения.

Случай 2 . Здесь q остается фиксированным, а p продвигается согласно нулю или нескольких применений правила 2, затем правила 4, потом правила 1 и, наконец, правила 2 в момент, когда p окажется внутри q . С этого момента оба ребра p и q могут продвигаться несколько раз, однако ребро q не может быть продвинуто за его следующую точку пересечения, пока p сначала не достигнет предыдущей точки пересечения ребра q (если этого уже не произошло с ребром p ). Поскольку p и q заканчиваются в одном и том же серпе, утверждение 1 гарантирует, что после некоторого числа дополнительных продвижений они пересекутся в точке пересечения, в которой заканчивается текущий серп.

Чтобы показать, что достаточно итераций для поиска некоторой точки пересечения, заметим, что при доказательстве утверждения 2 (о том, что граница полигона Q входит внутрь полигона P через ребро p при произвольном положении ребра q ) начальные позиции p и q достигались при выполнении не более итераций. Фактически такая ситуация, либо симметричная ей, в которой роли p и q взаимно заменяются, достигается за меньшее число итераций. Поскольку после этого ни p , ни q не будут продвинуты на полный оборот прежде, чем будет достигнута первая точка пересечения, потребуется не более дополнительных продвижений.

Рис. 6.17. Иллюстрация к доказательству, что можно найти точку пересечения, если границы P и Q пересекаются.

Урок 6. Как избежать попадания в экстремальную ситуацию.

Учебные вопросы.

1. Подготовка к походу.

2. Правила безопасного поведения на природе.

Цель. По окончании изучения темы учащиеся должны иметь представление об основных правилах поведения в природных условиях.

Основное содержание урока.

Как избежать попадания в экстремальные ситуации в природных условиях? Этот вопрос целесообразно рассмотреть на примере туристического похода школьников (класса).

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

Предложите ученикам разгадать слово, зашифрованное в ребусе (раздел 1, глава 3, задание 7).

Представление маршрута похода в маршрутно-квалификационную комиссию, регистрация группы и маршрута в поисково-спасательной службе (ПСС). Цель регистрации.

Соблюдение правил безопасного поведения на маршруте, на привале, при преодолении препятствий - основной этап обеспечения безопасности.

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

Предложите ученикам отгадать загадку:

Кто, как только жарко станет, Шубу на плечи натянет, А нагрянет холод злой -Скинет с плеч ее долой? (Лес)

Правило для тех, кто отправляется в разведку. Понятие «Границы полигона» и линейные ориентиры (дороги, просеки, граница леса, линии электропередачи). Для чего они определяются?

Заключение. Повторите основные моменты и проверьте, как понята тема.

Вопросы для проверки полученных знаний.

Объясните, какая основная цель тщательной подготовки к походу? Почему у руководителя группы абсолютная власть во время похода? Зачем руководитель группы сообщает маршрут похода и сроки его проведения в местную ПСС? Расскажите о правилах движения группы на маршруте. Почему последние километры дневного перехода считаются сложными? Как надо относиться к окружающей природе при движении на маршруте и на привале? Что такое «Границы полигона» и зачем они определяются?

Домашнее задание. Раздел 1, глава 3, темы 3.1 и 3.2.

Практические задания.

1. Отгадайте и правильно впишите в клеточки слова (задание 5 в конце темы 3.1.). Из букв, стоящих в серых клеточках, составьте слово, которое очень необходимо в походе.

2. Нарисуйте по памяти схему своего маршрута от дома до школы, от платформы электрички до дачи или еще какой-либо маршрут.

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

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


Пусть р будет монотонный полигон и переименуем его вершины как v 1 , v 2 ,..., v n в порядке увеличения координаты х, поскольку наш алгоритм будет обрабатывать эти вершины именно в таком порядке. Алгоритм формирует последовательность монотонных полигонов р = p 1 , p 2 ,... , p n = 0. Полигон p i , как результат обработки вершины v i , получается путем отсечения нуля или нескольких треугольников от предыдущего полигона p i-1 . Алгоритм заканчивает свою работу после выхода с p m , пустым полигоном, а коллекция треугольников, накопленная в процессе обработки, представляет собой триангуляцию исходного полигона р.

Алгоритм хранит стек s вершин, которые были проверены, но еще не обработаны полностью (возможно, обнаружены еще не все треугольники, связанные с этой вершиной). Перед началом обработки вершины v i в стеке содержатся некоторые вершины, принадлежащие полигону p i-1 . По мере выполнения алгоритма сохраняются определенные инварианты стека(Инвариантом называется состояние, существующее на определенной стадии работы алгоритма, например, в начале каждой итерации данного цикла). Пусть вершины в стеке обозначены как s 1 , s 2 ,..., s t в порядке снизу вверх, тогда могут встретиться четыре ситуации:

  1. s 1 , s 2 ,..., s t упорядочены по возрастанию координаты х и содержат каждую из вершин полигона p i-1 , расположенную справа от s 1 и слева от s t .
  2. s 1 , s 2 ,..., s t являются последовательными вершинами либо в верхней, либо в нижней цепочках полигона p i-1 .
  3. Вершины s 1 , s 2 ,..., s t являются вогнутыми вершинами полигона p i-1 (величина внутреннего угла при каждой из них превышает 180 градусов).
  4. Следующая подлежащая обработке вершина v i в полигоне p i-1 находится в следующем соотношении с вершинами s t и s 1:
    • a. v i соседняя с s t , но не с s 1 ,
    • б. v i соседняя с s 1 , но не с s t ,
    • в. v i соседняя с обеими вершинами s 1 и s t .

Все эти три случая условия 4 показаны на рис. 2.

Действия по обработке вершины v i будут различны для каждого из этих трех случаев, отражающих текущее состояние стека:

Рис. 2

Случай 4а. Вершина v i соседняя с s t , но не с s 1 : Если t > 1 и внутренний угол v i s t s t-1 меньше 180 градусов, то отсекается треугольник v i s t s t-1 и s t исключается из стека. После этого в стек заносится v i . В алгоритме используется тот факт, что угол v i s t s t-1 < 180 только в том случае, когда либо s t-1 лежит слева от вектора v i s t , если v i принадлежит полигону p i-1 из верхней цепочки, либо s t-1 лежит справа от вектора v i s t , если v i принадлежит нижней цепочке.

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

Случай 46. Вершина v i соседняя с s 1 , но не с s t . Отсекаем полигон, определяемый вершинами v i , s 1 , s 2 ,..., s t , очищаем стек и затем заносим в него сначала s t , потом v i . Полигон, определяемый вершинами v i , s 1 , s 2 ,..., s t фактически является веерообразным с узлом в точке v i (т. е. вершина v i принадлежит корню веера). После этого алгоритм выполняет триангуляцию полученного полигона.

Случай 4в.

Вершина v i соседняя с обеими вершинами s 1 и s t:

В этом случае v i = v n и полигон p i-1 , определяемый вершинами v i , s 1 , s 2 ,..., s t , является веерообразным с узлом в точке v n . Алгоритм непосредственно выполняет триангуляцию этого полигона и заканчивается.

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

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

Enum (UPPER, LOWER); List *triangulateMonotonePolygon(Polygon &p} { Stack s; List *triangles = new List; Vertex *v, *vu, *vl; leastVertex(p, leftToRightCmp); v = vu = vl = p.v(); s.push(v); int chain = advancePtr(vl, vu, v); s.push(v); while (1) { // внешний цикл while chain = advancePtr(vl, vu, v); if (adjacent(v, s.top()) && !adjacent(v, s . bottom ())) {// случай 4a int side = (chain == UPPER) ? LEFT: RIGHT; Vertex *a = s.top(); Vertex *b = s.nextToTop(); while ((s. size() > 1) && (b->classify(v->point() ,a->point()) == side)) { if (chain == UPPER) { p.setV(b); triangles->append (p.split(v)); } else { p.setv (v); triangles->append (p.split(b)); } s.pop(); a = b; b = s.nextToTop(); } s.push (v); } else if (! adjacent (v, s.top())) { // случай 4б Polygon *q; Vertex *t = s.pop(); if (chain == UPPER) { p.setV(t); q = p.split(v); } else { p.setV(v); q = p.split(t); q->advance(CLOCKWISE); } triangulateFanPolygon (*q, triangles); while (! s.empty ()) s.pop(); s.push(t); s.push(v); } else { // случай 4в p.setV (v); triangulateFanPolygon (p, triangles); return triangles; } } // конец внешнего цикла while }

Функция adjacent возвращает значение TRUE только в том случае, если две указанные в обращении вершины, являются соседними.

Bool adjacent (Vertex *v, Vertex *w) { return ((w == v->cw()) || (w = v->ccw())); }

Рис. 3

Программа triangulateMonotonePolygon при обработке полигона р анализирует одновременно верхнюю и нижнюю цепочки полигона, используя преимущества уже выполненного упорядочения вершин по возрастанию координаты х (в противном случае потребовалась бы дополнительная сортировка за время О(n log n)). На каждой итерации переменная v указывает на обрабатываемую вершину. В программе формируются еще две переменные vu и vl, которые указывают на последнюю обрабатываемую вершину в верхней и нижней цепочках полигона р соответственно. По мере работы программы эти указатели функцией advancePtr продвигаются слева направо. При каждом обращении к функции advancePtr она изменяет значение либо vu, либо vl и корректирует указатель v в соответствии с произведенным изменением:

Int advancePtr(Vertex* &vu, Vertex* &vl, Vertex* &v} { Vertex *vun = vu->cw(); Vertex *vln = vl->ccw(); if (vun->point() < vln->point()) { v = vu = vun; return UPPER; } else { v = vl = vln; return LOWER; } }

Функция advancePtr возвращает значение UPPER или LOWER, показывающее, к какой из двух цепочек вершин v относится произведенное действие. Это значение используется в программе triangulateMonotonePolygon для контроля, чтобы ее последующее обращение к функции split вызывало возврат части, выделенной из основного полигона, а не основного полигона, из которого эта часть исключена.

При триангуляции веерообразного полигона последовательно находятся все треугольники, имеющие одну корневую вершину и. Для этого полигон обходится, начиная с вершины v, и в каждой вершине w, не являющейся соседней с v, отсекается треугольник по хорде, соединяющей вершины v и w. Это выполняется функцией triangulateFanPolygon, которая деструктивно разбивает n-угольник р на n - 2 треугольников и добавляет их в список треугольников. В функции предполагается, что полигон р веерообразный с окном, расположенным на его корне:

Void triangulateFanPolygon(Polygon &p, List *triangles) { Vertex *w = p.v()->cw()->cw(); int size = p.size(); for (int i = 3; i < size; i++) { triangles->append(p.split(w)); w = w->cw(); } triangles->append(&p); }

На рис. 4 показана триангуляция, полученная с помощью описанного алгоритма. Монотонный полигон содержит 35 вершин.