Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Алгоритмы / Математика / Вычислительная геометрия /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Структуры геометрических данных, основные операции - Программирование от RIN.RU
Структуры геометрических данных, основные операции



Класс Polygon (Полигон)




Полигон представляется в виде объекта класса Polygon. Класс содержит два элемента данных. Первый из них, _v, указывает на некоторую вершину полигона - текущую позицию окна полигона. Большинство операций с полигонами относятся либо к текущему окну, либо к вершите в окне. Иногда мы будем ссылаться на вершину в окне как на текущую вершину. Во втором элементе данных, _size, хранится размер полигона:


class Polygon {
private:
Vertex *_v;
int _size;
void resize (void);
public:
Polygon (void);
Polygon (Polygon&);
Polygon (Vertex*);
~Polygon (void);
Vertex *v(void);
int size (void);
Point point (void);
Edge edge (void);
Vertex *cw(void);
Vertex *ccw (void);
Vertex *neighbor (int rotation);
Vertex *advance (int rotation);
Vertex *setV (Vertex*);
Vertex *insert (Point&);
void remove (void);
Polygon * split (Vertex*);
}




Конструкторы и деструкторы


Имеется несколько конструкторов для класса Polygon. Конструктор, не имеющий аргументов, инициирует пустой полигон



Polygon::Polygon(void) :
_v(NULL) , _size(0)
{
}


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


Polygon::Polygon (Polygon &р) {
_size = p._size;
if (_size == 0)
_v = NULL;
else {
_v = new Vertex (p.point());
for (int i = 1; i < _size; i++) {
p.advance(CLOCKWISE);
_v = _v->insert (new Vertex (p.point()));
}
p.advance (CLOCKWISE);
_v = _v->cw();
}
}


Третий конструктор инициализирует полигон с кольцевым двухсвязным списком вершин:


Polygon::Polygon (Vertex *v):
_v(v)
{
resize();
}


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


void Polygon::resize (void)
{
if (_v == NULL)
_size = 0;
else {
Vertex *v = _v->cw();
for (_size = 1; v != _v; ++_size, v = v->cw());
}
}


Деструктор ~Polygon освобождает вершины текущего полигона, прежде чем удалить сам объект Polygon:


Polygon::~Polygon (void)
{
if (_v) {
Vertex *w = _v- >cw();
while (_v != w) {
delete w->remove();
w = _v->cw();
}
delete _v;
}
}




Функции доступа


Следующие несколько компонентных функций позволяют получить некоторые сведения о текущем полигоне. Функция v возвращает текущую вершину данного полигона, а функция size - его размер:


Vertex *Polygon::v(void)
{
return _v;
}


int Polygon::size(void)
{
return _size;
}


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


Компонентная функция point возвращает точку на плоскости, которая соответствует текущей вершине. Компонентная функция edge возвращает текущее ребро. Текущее ребро начинается в текущей вершине и заканчивается в следующей после нее вершине:


Point Polygon::point (void)
{
return _v->point();
}


Edge Polygon::edge(void)
{
return Edge (point(), _v->cw()->point());
}




Класс Edge (ребро) будет определен в следующем разделе.


Компонентные функции cw и ccw возвращают последователя и предшественника для текущей вершины, оставляя без изменения положение окна. Функция neighbor (сосед) возвращает указатель на последователя или предшественника текущей вершины в зависимости от значения аргумента, с которым она вызывается (CLOCKWISE или COUNTER_CLOCKWISE - по часовой стрелке или против):


Vertex *Polygon::cw(void)
{
return _v->cw();
}


Vertex *Polygon::ccw(void)
{
return _v->ccw();
}


Vertex *Polygon::neighbor(int rotation)
{
return _v->neighbor(rotation);
}


Функции редактирования


Компонентные функции advance и setV перемещают окно на различные вершины. Функция advance (продвинуть) перемещает окно на последователя или предшественника текущей вершины в зависимости от заданного аргумента:


Vertex *Polygon::advance(int rotation)
{
return _v = _v->neighbor(rotation);
}


Компонентная функция setV перемещает окно на вершину v, указанную в качестве аргумента:


Vertex *Polygon::setV(Vertex *v)
{
return _v = v;
}




В программе должно быть обеспечено, чтобы вершина - v принадлежала текущему полигону.


Компонентная функция insert вносит новую вершину после текущей и затем перемещает окно на новую вершину:



Vertex *Polygon::insert(Point &p)
{
if (_size++ == 0)
_v = new Vertex(p);
else
_v = _v->insert(new Vertex(p));
return _v;
}


Компонентная функция remove удаляет текущую вершину. Окно перемещается на предшественника или остается неопределенным, если полигон становится пустым:


void Polygon::remove(void)
{
Vertex *v = _v;
_v = (--_size == 0) ? NULL : _v->ccw();
delete v->remove();
}


Расщепление полигонов


Расщепление полигонов заключается в делении полигонов на два меньших полигона. Разрезание осуществляется по некоторой хорде. Чтобы разрезать полигон вдоль хорды ab, вначале внесем после вершины а ее дубликат, а дубликат вершины b внесем перед вершиной b (эти дубликаты назовем ар и bр). Затем произведем разделение в вершинах а и bр. Этот процесс проиллюстрирован на рисунке ниже


Разрезание полигона вдоль хорды ab. Текущие вершины (в окне каждого полигона) обведены кружком


Компонентная функция Polygon::split определена в терминах функции Vertex::split. Эта последняя функция разделяет полигон вдоль хорды, соединяющей текущую вершину (которая играет роль вершины а) с вершиной b. Она возвращает указатель на вершину bр, являющуюся дублем вершины b:


Vertex *Vertex::split(Vertex *b)
{ // занесение bр перед вершиной b
Vertex *bр = b->ccw()->insert(new Vertex(b->point()));
insert(new Vertex(point()));
// занесение ар после текущей вершины
splice(bр);
return bр;
}


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


Polygon *Polygon::split (Vertex *b)
{
Vertex *bp = _v->split(b);
resize();
return new Polygon(bр);
}


Компонентная функция Polygon::split должна использоваться с некоторой осторожностью. Если вершина b является последователем текущей вершины _v, то после выполнения операции полигон остается неизменным. Если разрезание производится вдоль диагонали, не являющейся хордой, то один или даже оба результирующих 'полигона' могут оказаться взаимно пересекающимися. Если вершины b и _v принадлежат разным полигонам, то операция разделения приводит к соединению двух полигонов по двум совпадающим ребрам, которые соединяют эти две вершины.




Точки, входящие в состав выпуклого полигона


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


bool pointlnConvexPolygon (Point &s , Polygon &p)
{
if (p. size() == 1)
return (s == p.point());
if (p. size () == 2) {
int c = s .classify (p. edge ());
return ( (c==BETWEEN) || (c==ORIGIN) | || (c==DESTINATION) );
}
Vertex *org = p.v();
for (int i = 0; i < p.size(); i++, p.advance(CLOCKWISE))
if (s.classify (p.edge()) == LEFT) {
p.setV(org);
return FALSE;
}
return TRUE;
}


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


Определение наименьшей вершины в полигоне


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



Vertex *leastVertex( Polygon &p, int (*cmp)(Point*,Point*) )
{
Vertex *bestV = p.v();
p.advance(CLOCKWISE);
for (int i=1; i < p.size(); p.advance(CLOCKWISE), i++)
if ((*cmp)(p.v(),bestV) < 0)
bestV = p.v();
p.setV(bestV);
return bestV;
}


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


int leftToRightCmp (Point *a, Point *b)
{
if (*a < *b) return -1;
if (*a > *b) return 1;
return 0;
}


Для нахождения самой правой вершины можно использовать такую функцию сравнения:


int rightToLeftCmp(Point *a, Point *b)
{
return leftToRightCmp(b, a);
}




<<<  НазадВперед  >>>
 1  2  3  4 


 8  Комментарии к статье  8 8  Обсудить в чате

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь