// Ryzom - MMORPG Framework
// 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 .
#ifndef NL_MOVE_GRID_H
#define NL_MOVE_GRID_H
#include "nel/misc/types_nl.h"
#include "nel/misc/vector.h"
#include "nel/misc/aabbox.h"
#include "nel/misc/block_memory.h"
#include "nel/misc/stream.h"
/**
* A list of object that must have Next and Previous pointers
* \param T the type of item
* \param TPtr a pointer to T to use in list (useful for smartpointer)
*/
template
class CMoveGridObjectList
{
public:
TT *Head;
TT *Tail;
CMoveGridObjectList() : Head(NULL), Tail(NULL) {}
void insertAtHead(TT *object)
{
nlassert(object->Next == NULL);
nlassert(object->Previous == NULL);
object->Next = Head;
if (object->Next != NULL)
object->Next->Previous = object;
else
Tail = object;
Head = object;
}
void insertAtTail(TT *object)
{
nlassert(object->Next == NULL);
nlassert(object->Previous == NULL);
object->Previous = Tail;
if (object->Previous != NULL)
object->Previous->Next = object;
else
Head = object;
Tail = object;
}
void remove(TT *object)
{
// if object at head
if (object->Previous == NULL)
Head = object->Next;
else
object->Previous->Next = object->Next;
// if object at tail
if (object->Next == NULL)
Tail = object->Previous;
else
object->Next->Previous = object->Previous;
object->Previous = NULL;
object->Next = NULL;
}
TT *getHead() { return (TT*)Head; }
TT *getTail() { return (TT*)Tail; }
};
/**
* Move grid, allows to select moving entities fast (template parameter CSIZE is in millimeter)
* \author Benjamin Legros
* \author Nevrax France
* \date 2001
*/
template
class CMoveGrid
{
public:
class CIterator;
friend class CIterator;
protected:
class CCellNode;
class CNode;
public:
/// Constructor
CMoveGrid();
/// Destructor
~CMoveGrid();
/// Clear
void clear();
/// Insert an element in move grid. Return an iterator towards the inserted object
CIterator insert(const T &object, const NLMISC::CVector &position);
/// Move an object in grid.
void move(CIterator &it, const NLMISC::CVector &position);
/// Remove
void remove(CIterator &it);
void insertNode(CIterator &it, sint x, sint y);
void removeNode(CIterator &it);
/// select
void select(const NLMISC::CVector &position);
/// select
void select(const NLMISC::CAABBox &bbox);
/// begin
CIterator begin();
/// end
CIterator end();
/// clearSelection
void clearSelection();
/// round value so it is a half of a segment
double round(double v)
{
const double INV = 1000.0 / (double)CSIZE;
return (floor(v*INV)+0.5)*(double)CSIZE*0.001;
}
/// Serial
void serial(NLMISC::IStream &f);
protected:
sint convert(float v)
{
const float INV = 1000.0f / (float)CSIZE;
return (sint)(v*INV);
}
sint convertGrid(float v)
{
const float INV = 1000.0f / (float)CSIZE;
return (sint)(v*INV)&(CELLS-1);
}
sint convertGrid(sint v)
{
return v&(CELLS-1);
}
void select(sint X, sint Y);
protected:
/// A node that contains an objects.
typedef CMoveGridObjectList TNodeList;
/// A node that is a cell, containing a list of node inside this cell.
typedef CMoveGridObjectList TCellNodeList;
class CCellNode
{
public:
sint32 X, Y;
sint32 GridX, GridY;
TNodeList NodeList;
CCellNode *Previous, *Next;
CCellNode *Selection;
CCellNode() : X(0), Y(0), GridX(0), GridY(0), Previous(NULL), Next(NULL), Selection(NULL) {}
void serial(NLMISC::IStream &f)
{
f.serial(X, Y, GridX, GridY);
}
};
class CNode
{
public:
CNode *Previous, *Next;
CCellNode *Root;
T Object;
CNode() : Previous(NULL), Next(NULL), Root(NULL) {}
void serial(NLMISC::IStream &f)
{
f.serial(Object);
}
};
/// The map of cell nodes
TCellNodeList _Grid[CELLS][CELLS];
/// The first selected cell node
CCellNode *_Selection;
/// The allocator of nodes
NLMISC::CBlockMemory _NodeAllocator;
/// The allocator of cell nodes
NLMISC::CBlockMemory _CellNodeAllocator;
public:
class CIterator
{
friend class CMoveGrid;
public:
CIterator(CNode *node = NULL) : _Node(node) {}
CIterator(const CIterator &it) : _Node(it._Node) {}
T & operator * () { return _Node->Object; }
CIterator & operator ++ ()
{
if (_Node->Next != NULL)
{
_Node = _Node->Next;
}
else
{
CCellNode *nextCell = _Node->Root->Selection;
_Node = NULL;
// iterate till we have a non empty selected cell
while (nextCell != NULL && (_Node = nextCell->NodeList.getHead()) == NULL)
nextCell = nextCell->Selection;
}
return *this;
}
CIterator operator ++ (int)
{
CIterator ret(_Node);
++(*this);
return ret;
}
bool operator == (const CIterator &it) const { return it._Node == _Node; }
bool operator != (const CIterator &it) const { return !(*this == it); }
CIterator operator = (const CIterator &it) { _Node = it._Node; return *this; }
protected:
CNode *_Node;
};
};
//
// TEMPLATE IMPLEMENTATION
//
// Serial
template
void CMoveGrid::serial(NLMISC::IStream &f)
{
sint version = f.serialVersion(0);
f.serialCheck((sint32)CELLS);
f.serialCheck((sint32)CSIZE);
if (f.isReading())
{
clear();
sint i, j;
uint32 nc, nn;
for (i=0; i 0)
{
cellNode = _CellNodeAllocator.allocate();
f.serial(*cellNode);
list.insertAtTail(cellNode);
f.serial(nn);
while (nn-- > 0)
{
node = _NodeAllocator.allocate();
f.serial(*node);
cellNode->NodeList.insertAtTail(node);
node->Root = cellNode;
}
}
}
}
}
else
{
sint i, j;
uint32 nc, nn;
for (i=0; iNext;
f.serial(nc);
cellNode = list.getHead();
while (cellNode != NULL)
{
f.serial(*cellNode);
node = cellNode->NodeList.getHead();
nn = 0;
while (node != NULL)
++nn, node = node->Next;
f.serial(nn);
node = cellNode->NodeList.getHead();
while (node != NULL)
{
f.serial(*node);
node = node->Next;
}
cellNode = cellNode->Next;
}
}
}
}
}
//
template
void CMoveGrid::clear()
{
sint i, j;
for (i=0; iNodeList.getHead()) != NULL)
{
cellNode->NodeList.remove(node);
_NodeAllocator.free(node);
}
list.remove(cellNode);
_CellNodeAllocator.free(cellNode);
}
}
}
clearSelection();
}
//
template
CMoveGrid::CMoveGrid()
{
_Selection = NULL;
}
//
template
CMoveGrid::~CMoveGrid()
{
clear();
}
//
template
void CMoveGrid::insertNode(CIterator &it, sint x, sint y)
{
sint gridX, gridY;
gridX = convertGrid(x);
gridY = convertGrid(y);
CCellNode *cellNode = _Grid[gridX][gridY].getHead();
while (cellNode != NULL && (cellNode->X != x || cellNode->Y != y))
cellNode = cellNode->Next;
if (cellNode == NULL)
{
cellNode = _CellNodeAllocator.allocate();
_Grid[gridX][gridY].insertAtHead(cellNode);
cellNode->X = x;
cellNode->Y = y;
cellNode->GridX = gridX;
cellNode->GridY = gridY;
}
it._Node->Root = cellNode;
cellNode->NodeList.insertAtHead(it._Node);
}
//
template
void CMoveGrid::removeNode(CIterator &it)
{
it._Node->Root->NodeList.remove(it._Node);
}
//
template
typename::CMoveGrid::CIterator CMoveGrid::insert(const T &object, const NLMISC::CVector &position)
{
CNode *node = _NodeAllocator.allocate();
sint X, Y;
X = convert(position.x);
Y = convert(position.y);
node->Object = object;
CIterator it(node);
insertNode(it, X, Y);
return CIterator(node);
}
template
void CMoveGrid::move(CIterator &it, const NLMISC::CVector &position)
{
sint X, Y;
X = convert(position.x);
Y = convert(position.y);
CNode *node = it._Node;
if (X == node->Root->X && Y == node->Root->Y)
return;
removeNode(it);
insertNode(it, X, Y);
}
template
void CMoveGrid::remove(CIterator &it)
{
CNode *node = it._Node;
removeNode(it);
it._Node = NULL;
_NodeAllocator.free(node);
}
template
void CMoveGrid::select(const NLMISC::CVector &position)
{
select(convert(position.x), convert(position.y));
}
template
void CMoveGrid::select(const NLMISC::CAABBox &bbox)
{
sint x0, x1;
sint y0, y1;
x0 = convert(bbox.getCenter().x-bbox.getHalfSize().x);
x1 = convert(bbox.getCenter().x+bbox.getHalfSize().x);
y0 = convert(bbox.getCenter().y-bbox.getHalfSize().y);
y1 = convert(bbox.getCenter().y+bbox.getHalfSize().y);
sint x, y;
for (y=y0; y<=y1; ++y)
for (x=x0; x<=x1; ++x)
select(x, y);
}
template
void CMoveGrid::select(sint x, sint y)
{
sint gx = convertGrid(x),
gy = convertGrid(y);
CCellNode *cellNode = _Grid[gx][gy].getHead();
while (cellNode != NULL && (cellNode->X != x || cellNode->Y != y))
cellNode = cellNode->Next;
if (cellNode != NULL && cellNode->NodeList.getHead() != NULL)
{
cellNode->Selection = _Selection;
_Selection = cellNode;
}
}
template
typename::CMoveGrid::CIterator CMoveGrid::begin()
{
return (_Selection != NULL) ? CIterator(_Selection->NodeList.getHead()) : CIterator(NULL);
}
template
typename::CMoveGrid::CIterator CMoveGrid::end()
{
return CIterator(NULL);
}
template
void CMoveGrid::clearSelection()
{
CCellNode *cellNode = _Selection;
_Selection = NULL;
while (cellNode != NULL)
{
CCellNode *nd = cellNode;
cellNode = cellNode->Selection;
nd->Selection = NULL;
}
}
#endif // NL_MOVE_GRID_H
/* End of move_grid.h */