4. Описание программы.
4.1 Классы.
Квадрантное дерево представлено в программе классом QTree:
class QTree {
private:
QTreeNode *m_root; //корень дерева
Rect m_domain; //область поиска
//осуществляет построение дерева
QTreeNode *BuildQTree(Grid &G,int M,int D,int level,int,int,int,int);
public:
//конструкторы
QTree(void);
QTree(Grid &G,int M,int D);
//деструктор
~QTree();
//создание новой структуры дерева
void Create(Grid &G,int M,int D);
//запрос по области
List *RangeQuery(Rect &range);
//отображение сетки построенного дерева
void DrawQTree(CDC*);
};
Узлы дерева представляются в виде объектов класса QTreeNode:
class QTreeNode {
private:
QTreeNode *child[4]; //дочерние поддеревья
List *pts; //указатель на список точек, покрываемых
//данным квадрантом
int m_size; //размер данного квадранта
//запрос по области
List *RangeQuery(Rect &range,Rect &quadrant);
//вычисление квадранта, накрываемого данным узлом
Rect Quadrant(Rect &s,int i);
//является ли текущий узел внешним
int isExternal();
//отображение квадранта, накрываемого данным узлом
void Draw(Rect&,CDC*);
public:
//конструкторы
QTreeNode(List*);
QTreeNode(void);
//деструктор
~QTreeNode(void);
friend class QTree;
};
Конструктор QTree(void) создает пустое дерево. Конструктору QTree(Grid& G,int M,int D) передается сетка G, коэффициент заполняемости ячеек M и предельная глубина D. Данный конструктор формирует квадрантное дерево по точкам в сетке G, обеспечивая ограничения, налагаемые значениями M и D. Предполагается, что количество ячеек вдоль каждой стороны сетки G равно степени 2.
QTree::QTree(Grid &G,int M,int D):
m_root(BuildQTree(G,M,D,0,0,G.m-1,0,G.m-1)),
m_domain(Rect(ELEMENT(0,0),ELEMENT(G.m*G.m_cellSize,G.m*G.m_cellSize)))
{
}
Компонентная функция BuildQTree строит квадрантное дерево по сетке G снизу вверх, от уровня отдельной ячейки сетки до нулевого уровня, при котором домен D рассматривается как единая ячейка. На самом нижнем уровне – уровне k, когда сетка имеет размер 2k2k – каждая ячейка сетки представлена своим собственным одноузловым квадрантным деревом. Узел содержит список тех точек из набора S, которые попадают в его ячейку. Для построения квадрантного дерева, корни которого лежат на уровне h, будем компоновать группы из 4-х квадрантных деревьев, корни которых лежат на следующем более низком уровне h+1. Когда достигнем уровня 0, самого верхнего уровня, то получим единое квадрантное дерево, которое накрывает весь домен D целиком. Каждый из внешних узлов дерева будет содержать список всех точек набора S, которые он накрывает.
В компонентную функцию BuildQTree передается сетка G, коэффициент заполняемости ячеек M и предельная глубина D. Она формирует квадрантное дерево для подсетки G[imin..imax,jmin..jmax] и возвращает его корневой узел. Параметр level указывает уровень, на котором расположен этот корневой узел внутри основного квадрантного дерева верхнего уровня.
QTreeNode *QTree::BuildQTree(Grid &G,int M,int D,int level,int imin,int imax,int jmin,int jmax)
{
if (imin==imax)
{
QTreeNode *q=new QTreeNode(G.m_pGrid[imin][jmin]);
G.m_pGrid[imin][jmin]=new List;
return q;
}
else
{
QTreeNode *p=new QTreeNode;
int imid=(imin+imax)/2;
int jmid=(jmin+jmax)/2;
p->child[0]=BuildQTree(G,M,D,level+1,imid+1,imax,jmid+1,jmax);
p->child[1]=BuildQTree(G,M,D,level+1,imid+1,imax,jmin,jmid);
p->child[2]=BuildQTree(G,M,D,level+1,imin,imid,jmin,jmid);
p->child[3]=BuildQTree(G,M,D,level+1,imin,imid,jmid+1,jmax);
for(int i=0;i<4;i++) p->m_size+=p->child[i]->m_size;
if ((p->m_size<=M)||(level>=D))
{
p->pts=new List;
for(int j=0;j<4;j++)
{
for(p->child[j]->pts->First();
!p->child[j]->pts->isHead();
p->child[j]->pts->Next())
p->pts->Append(new ELEMENT(*(p->child[j]->pts->Val())));
delete p->child[j];
p->child[j]=NULL;
}
};
return p; }}
В общем случае функция BuildQTree рекурсивно образует корень p для четырех его потомков и связывает их с узлом p.
Первый конструктор QTreeNode инициализирует свои элементы данных класса при передачи ему списка точек:
QTreeNode::QTreeNode(List *_pts):
pts(_pts),m_size(_pts->Lenght())
{
for(int i=0;i<4;i++)
child[i]=NULL;
}
Конструктор QTreeNode, не имеющий аргументов, используется при формировании списка точек внутреннего узла внутри компонентной функции BuildQTree.
QTreeNode::QTreeNode(void):
pts(NULL),m_size(0)
{
for(int i=0;i<4;i++)
child[i]=NULL;
}
Для осуществления запроса по заданной области R выполняется запрос к корневому узлу квадрантного дерева:
List *QTree::RangeQuery(Rect &r)
{
return m_root->RangeQuery(r,m_domain);
}
Для запроса по области R, начинающейся с узла n, необходимо:
1. Проверить, пересекается ли область R с областью (n). Если нет, то выполнить возврат.
{
List *result=new List;
if (!Intersect(r,span))
return result;
else if (isExternal())
for(pts->First();!pts->isHead();pts->Next())
{
ELEMENT *p=pts->Val();
if ((p->x<=r.ne.x)&&(p->x>=r.sw.x) &&
(p->y<=r.ne.y)&&(p->y>=r.sw.y))
result->Append(new ELEMENT(*p));
}
else
for(int i=0;i<4;i++)
{
List *l=child[i]->RangeQuery(r,Quadrant(span,i));
for(l->First();!l->isHead();l->Next())
result->Append(new ELEMENT(*(l->Val())));
}
return result;
}
Параметр span определяет квадрант, накрываемый текущим узлом.
4.2 Интерфейс.
Набор исходных точек можно задать двумя способами: непосредственно нажатием левой клавиши мыши в окне программы или генерацией точек случайным способом.
Дерево строится автоматически при изменении набора точек.
Определение области поиска можно произвести нажатием правой кнопки мыши, ее удерживанием и перемещением курсора мыши в новую точку экрана.
Сам поиск внутри выделенной области происходит по команде “Поиск”.
Результаты поиска можно наблюдать непосредственно на экране: найденные точки будут выделяться более светлым цветом, чем все остальные.
Предусмотрена возможность просмотра процедуры построения дерева по шагам (режимы просмотра вперед и назад, а также режим ускоренного просмотра: текущий узел дерева отображается на экране квадратной областью желтого цвета).
Предусмотрена возможность просмотра процедуры поиска по области по шагам (режимы просмотра вперед и назад, а также режим ускоренного просмотра: текущая область, которая проверяется на пересечение с областью поиска отображается на экране квадратной областью желтого цвета, а текущая точка, проверяемая на попадание в область поиска – более крупными размерами).
В главном окне приложения также можно менять следующие параметры:
а) минимальное количество точек в квадранте («Минимальное наполнение»)
г) Максимальная глубина дерева («Максимальная глубина»).
Программа позволяет создавать новый документ (Файл/Создать), сохранять текущий (Файл/Сохранить, Файл/Сохранить как), загружать ранее сохраненный (Файл/Открыть), а также сохранять в графическом файле текущий вид рабочей области (Файл/Сохранить скриншот).
4.3 Внешний вид программы