khanat-opennel-code/code/nel/include/nel/pacs/quad_grid.h

548 lines
14 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NLPACS_QUAD_GRID_H
#define NLPACS_QUAD_GRID_H
#include "nel/misc/debug.h"
#include "nel/misc/vector.h"
#include "nel/misc/plane.h"
#include "nel/misc/matrix.h"
#include <list>
#include <vector>
namespace NLPACS
{
using NLMISC::CVector;
// ***************************************************************************
/**
* This container is a simple grid, used to quickly find elements. His purpose is similiar to CQuadTree, but
* it is a simple grid, so test are in O(1), not in O(log n). It is perfect for local lookup (like in collisions).
* Use it if you want to select small area, not large. Also, for best use, elements should have approximatively the
* same size, and this size should be little smaller than the size of a grid element...
*
* By default, the quad grid is aligned on XY. (unlike the quadtree!!!)
*
* Unlike the quadtree, the quadgrid is NOT geographicly delimited, ie, its limits "tiles"!! This is why no "center"
* is required. As a direct consequence, when you select something, you are REALLY not sure that what you select is not
* a mile away from your selection :) ....
*
* Also, for memory optimisation, no bbox is stored in the quadgrid. Hence no particular selection is made on the Z
* components...
*
* \author Lionel Berenguier
* \author Nevrax France
* \date 2000
*/
template<class T> class CQuadGrid
{
public:
/// Iterator of the contener
class CIterator;
friend class CIterator;
public:
/// Default constructor, use axes XY!!!, has a size of 16, and EltSize is 1.
CQuadGrid();
/// dtor.
~CQuadGrid();
/// \name Initialization
//@{
/** Change the base matrix of the quad grid. For exemple this code init the grid tree in the plane XZ:
* \code
* CQuadGrid grid;
* NLMISC::CMatrix tmp;
* NLMISC::CVector I(1,0,0);
* NLMISC::CVector J(0,0,1);
* NLMISC::CVector K(0,-1,0);
*
* tmp.identity();
* tmp.setRot(I,J,K, true);
* quadTree.changeBase (tmp);
* \endcode
*
* \param base Base of the quad grid
*/
void changeBase(const NLMISC::CMatrix& base);
/** Init the container. container is first clear() ed.
*
* \param size is the width and the height of the initial quad tree, in number of square.
* For performance view, this should be a power of 2, and <=32768. (eg: 256,512, 8, 16 ...)
* \param eltSize is the width and height of an element. Must be >0. Notice that the quadgrid MUST be square!!
*/
void create(uint size, float eltSize);
//@}
/// \name Container operation
//@{
/// Clear the container. Elements are deleted, but the quadgrid is not erased.
void clear();
/** Erase an interator from the container
*
* \param it is the iterator to erase.
* \return if element is currently selected, the next selected element is returned, (or end()).
* if the element is not selected, end() is returned.
*/
CIterator erase(CIterator it);
/** Insert a new element in the container.
*
* \param bboxmin is the corner of the bounding box of the element to insert with minimal coordinates.
* \param bboxmax is the corner of the bounding box of the element to insert with maximal coordinates.
* \param val is a reference on the value to insert.
*/
CIterator insert(const NLMISC::CVector &bboxmin, const NLMISC::CVector &bboxmax, const T &val);
//@}
/// \name Selection
//@{
/** Clear the selection list
*/
void clearSelection();
/** Select all the container
*/
void selectAll();
/** Select element intersecting a bounding box. Clear the selection first.
*
* \param bboxmin is the corner of the bounding box used to select
* \param bboxmax is the corner of the bounding box used to select
*/
void select(const NLMISC::CVector &bboxmin, const NLMISC::CVector &bboxmax);
/** Return the first iterator of the selected element list. begin and end are valid till the next insert.
*/
CIterator begin();
/** Return the end iterator of the selected element list. begin and end are valid till the next insert.
*/
CIterator end();
//@}
// =================
// =================
// IMPLEMENTATION.
// =================
// =================
private:// Classes.
class CBaseNode
{
public:
CBaseNode *Prev,*Next; // For selection.
CBaseNode() {Prev= Next= NULL;}
};
class CNode : public CBaseNode
{
public:
T Elt;
uint16 x0,x1; // The location of the elt in the grid. Used for erase().
uint16 y0,y1;
};
class CQuadNode
{
public:
std::list<CNode*> Nodes;
};
private:// Atttributes.
std::vector<CQuadNode> _Grid;
sint _Size;
sint _SizePower;
float _EltSize;
NLMISC::CMatrix _ChangeBasis;
// Selection.
CBaseNode _Selection;
private:// Methods.
// return the coordinates on the grid of what include the bbox.
void selectQuads(CVector bmin, CVector bmax, sint &x0, sint &x1, sint &y0, sint &y1)
{
CVector bminp, bmaxp;
bminp= bmin;
bmaxp= bmax;
bmin.minof(bminp, bmaxp);
bmax.maxof(bminp, bmaxp);
bmin/= _EltSize;
bmax/= _EltSize;
x0= (sint)(floor(bmin.x));
x1= (sint)(ceil(bmax.x));
y0= (sint)(floor(bmin.y));
y1= (sint)(ceil(bmax.y));
// Very special case where the bbox.size==0 AND position is JUST on an edge of a case.
if(x0==x1)
x1++;
if(y0==y1)
y1++;
// Manage tiling.
if(x1-x0>=_Size)
x0=0, x1= _Size;
else
{
x0&= _Size-1;
x1&= _Size-1;
if(x1<=x0)
x1+=_Size;
}
if(y1-y0>=_Size)
y0=0, y1= _Size;
else
{
y0&= _Size-1;
y1&= _Size-1;
if(y1<=y0)
y1+=_Size;
}
}
// If not done, add the node to the selection.
void addToSelection(CNode *ptr)
{
if(ptr->Prev==NULL)
{
// Append to front of the list.
ptr->Prev= &_Selection;
ptr->Next= _Selection.Next;
if(_Selection.Next)
_Selection.Next->Prev= ptr;
_Selection.Next= ptr;
}
}
// Try to add each node of the quad node list.
void addQuadNodeToSelection(CQuadNode &quad)
{
typename std::list<CNode*>::iterator itNode;
for(itNode= quad.Nodes.begin();itNode!=quad.Nodes.end();itNode++)
{
addToSelection(*itNode);
}
}
public:
// CLASS const_iterator.
class const_iterator
{
public:
const_iterator() {_Ptr=NULL;}
const_iterator(CNode *p) : _Ptr(p) {}
const_iterator(const CIterator& x) : _Ptr(x._Ptr) {}
const T& operator*() const
{return _Ptr->Elt; }
// Doesn't work...
/*const T* operator->() const
{return (&**this); }*/
const_iterator& operator++()
{_Ptr = (CNode*)(_Ptr->Next); return (*this); }
const_iterator operator++(int)
{const_iterator tmp = *this; ++*this; return (tmp); }
const_iterator& operator--()
{_Ptr = (CNode*)(_Ptr->Prev); return (*this); }
const_iterator operator--(int)
{const_iterator tmp = *this; --*this; return (tmp); }
bool operator==(const const_iterator& x) const
{return (_Ptr == x._Ptr); }
bool operator!=(const const_iterator& x) const
{return (!(*this == x)); }
protected:
CNode *_Ptr;
friend class CQuadGrid<T>;
friend class CIterator;
};
// CLASS CIterator
class CIterator : public const_iterator
{
public:
CIterator() {this->_Ptr=NULL;}
CIterator(CNode *p) : const_iterator(p) {}
T& operator*() const
{return this->_Ptr->Elt; }
// Doesn't work...
/*T* operator->() const
{return (&**this); }*/
CIterator& operator++()
{this->_Ptr = (CNode*)(this->_Ptr->Next); return (*this); }
CIterator operator++(int)
{CIterator tmp = *this; ++*this; return (tmp); }
CIterator& operator--()
{this->_Ptr = (CNode*)(this->_Ptr->Prev); return (*this); }
CIterator operator--(int)
{CIterator tmp = *this; --*this; return (tmp); }
bool operator==(const const_iterator& x) const
{return (this->_Ptr == x._Ptr); }
bool operator!=(const const_iterator& x) const
{return (!(*this == x)); }
protected:
friend class CQuadGrid<T>;
};
};
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// Template CQuadGrid implementation.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// Init.
// ***************************************************************************
// ***************************************************************************
template<class T> CQuadGrid<T>::CQuadGrid()
{
_SizePower=4;
_Size=1<<_SizePower;
_EltSize=1;
_ChangeBasis.identity();
}
// ***************************************************************************
template<class T> CQuadGrid<T>::~CQuadGrid()
{
clear();
}
// ***************************************************************************
template<class T> void CQuadGrid<T>::changeBase(const NLMISC::CMatrix& base)
{
_ChangeBasis= base;
}
// ***************************************************************************
template<class T> void CQuadGrid<T>::create(uint size, float eltSize)
{
clear();
nlassert(NLMISC::isPowerOf2(size));
nlassert(size<=32768);
_SizePower= NLMISC::getPowerOf2(size);
_Size=1<<_SizePower;
_Grid.resize(_Size*_Size);
nlassert(eltSize>0);
_EltSize= eltSize;
}
// ***************************************************************************
// insert/erase.
// ***************************************************************************
// ***************************************************************************
template<class T> void CQuadGrid<T>::clear()
{
CIterator it;
selectAll();
while( (it=begin())!=end())
{
erase(it);
}
// Clear the selection...
_Selection.Next= NULL;
}
// ***************************************************************************
template<class T> typename CQuadGrid<T>::CIterator CQuadGrid<T>::erase(typename CQuadGrid<T>::CIterator it)
{
sint x,y;
CNode *ptr= it._Ptr;
if(!ptr)
return end();
// First erase all references to it.
//==================================
for(y= ptr->y0;y<ptr->y1;y++)
{
sint xe,ye;
ye= y &(_Size-1);
for(x= ptr->x0;x<ptr->x1;x++)
{
xe= x &(_Size-1);
CQuadNode &quad= _Grid[(ye<<_SizePower)+xe];
typename std::list<CNode*>::iterator itNode;
for(itNode= quad.Nodes.begin();itNode!=quad.Nodes.end();itNode++)
{
if((*itNode)==ptr)
{
quad.Nodes.erase(itNode);
break;
}
}
}
}
// Then delete it..., and update selection linked list.
//=====================================================
// if selected.
CBaseNode *next= NULL;
if(ptr->Prev)
{
next= ptr->Next;
if(next)
next->Prev=ptr->Prev;
ptr->Prev->Next= next;
}
delete ptr;
return CIterator((CNode*)next);
}
// ***************************************************************************
template<class T> typename CQuadGrid<T>::CIterator CQuadGrid<T>::insert(const NLMISC::CVector &bboxmin, const NLMISC::CVector &bboxmax, const T &val)
{
CVector bmin,bmax;
bmin= _ChangeBasis*bboxmin;
bmax= _ChangeBasis*bboxmax;
CNode *ptr= new CNode;
ptr->Elt= val;
// Find which quad include the object.
//===================================
sint x0,y0;
sint x1,y1;
selectQuads(bmin, bmax, x0,x1, y0,y1);
ptr->x0= uint16(x0);
ptr->x1= uint16(x1);
ptr->y0= uint16(y0);
ptr->y1= uint16(y1);
// Then for all of them, insert the node in their list.
//=====================================================
sint x,y;
for(y= ptr->y0;y<ptr->y1;y++)
{
sint xe,ye;
ye= y &(_Size-1);
for(x= ptr->x0;x<ptr->x1;x++)
{
xe= x &(_Size-1);
CQuadNode &quad= _Grid[(ye<<_SizePower)+xe];
quad.Nodes.push_back(ptr);
}
}
return CIterator(ptr);
}
// ***************************************************************************
// selection.
// ***************************************************************************
// ***************************************************************************
template<class T> void CQuadGrid<T>::clearSelection()
{
CBaseNode *ptr= _Selection.Next;
while(ptr)
{
ptr->Prev= NULL;
CBaseNode *next= ptr->Next;
ptr->Next= NULL;
ptr= next;
}
_Selection.Next= NULL;
}
// ***************************************************************************
template<class T> void CQuadGrid<T>::selectAll()
{
clearSelection();
for(sint i=0;i<(sint)_Grid.size();i++)
{
CQuadNode &quad= _Grid[i];
addQuadNodeToSelection(quad);
}
}
// ***************************************************************************
template<class T> void CQuadGrid<T>::select(const NLMISC::CVector &bboxmin, const NLMISC::CVector &bboxmax)
{
CVector bmin,bmax;
bmin= _ChangeBasis*bboxmin;
bmax= _ChangeBasis*bboxmax;
clearSelection();
// What are the quads to access?
sint x0,y0;
sint x1,y1;
selectQuads(bmin, bmax, x0,x1, y0,y1);
sint x,y;
for(y= y0;y<y1;y++)
{
sint xe,ye;
ye= y &(_Size-1);
for(x= x0;x<x1;x++)
{
xe= x &(_Size-1);
CQuadNode &quad= _Grid[(ye<<_SizePower)+xe];
addQuadNodeToSelection(quad);
}
}
}
// ***************************************************************************
template<class T> typename CQuadGrid<T>::CIterator CQuadGrid<T>::begin()
{
return CIterator((CNode*)(_Selection.Next));
}
// ***************************************************************************
template<class T> typename CQuadGrid<T>::CIterator CQuadGrid<T>::end()
{
return CIterator(NULL);
}
} // NLPACS
#endif // NLPACS_QUAD_GRID_H
/* End of quad_grid.h */