508 lines
16 KiB
C++
508 lines
16 KiB
C++
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004-2016 \/)\/ *
|
|
* Visual Computing Lab /\/| *
|
|
* ISTI - Italian National Research Council | *
|
|
* \ *
|
|
* All rights reserved. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 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 General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
|
* for more details. *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
#ifndef __VCGLIB_UGRID
|
|
#define __VCGLIB_UGRID
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <vcg/space/box3.h>
|
|
#include <vcg/space/line3.h>
|
|
#include <vcg/space/index/grid_util.h>
|
|
#include <vcg/space/index/grid_closest.h>
|
|
#include <vcg/simplex/face/distance.h>
|
|
|
|
namespace vcg {
|
|
|
|
/** Static Uniform Grid
|
|
A spatial search structure for a accessing a container of objects.
|
|
It is based on a uniform grid overlayed over a protion of space.
|
|
The grid partion the space into cells. Cells contains just pointers
|
|
to the object that are stored elsewhere.
|
|
The set of objects is meant to be static and pointer stable.
|
|
|
|
Useful for situation were many space related query are issued over
|
|
the same dataset (ray tracing, measuring distances between meshes,
|
|
re-detailing ecc.).
|
|
Works well for distribution that ar reasonably uniform.
|
|
How to use it:
|
|
ContainerType must have a 'value_type' typedef inside.
|
|
(stl containers already have it)
|
|
|
|
Objects pointed by cells (of kind 'value_type') must have
|
|
a 'ScalarType' typedef (float or double usually)
|
|
and a member function:
|
|
|
|
void GetBBox(Box3<ScalarType> &b)
|
|
which return the bounding box of the object
|
|
|
|
When using the GetClosest() method, the user must supply a functor object
|
|
(whose type is a method template argument) which expose the following
|
|
operator ():
|
|
|
|
bool operator () (const ObjType & obj, const Point3f & point, ScalarType & mindist, Point3f & result);
|
|
which return true if the distance from point to the object 'obj' is < mindist
|
|
and set mindist to said distance, and result must be set as the closest
|
|
point of the object to point)
|
|
*/
|
|
|
|
template < class OBJTYPE, class FLT=float >
|
|
class GridStaticPtr: public BasicGrid<FLT>, SpatialIndex<OBJTYPE,FLT>
|
|
{
|
|
public:
|
|
typedef OBJTYPE ObjType;
|
|
typedef ObjType* ObjPtr;
|
|
typedef typename ObjType::ScalarType ScalarType;
|
|
typedef Point3<ScalarType> CoordType;
|
|
typedef Box3<ScalarType> Box3x;
|
|
typedef Line3<ScalarType> Line3x;
|
|
typedef GridStaticPtr<OBJTYPE,FLT> GridPtrType;
|
|
typedef BasicGrid<FLT> BT;
|
|
|
|
/** Internal class for keeping the first pointer of object.
|
|
Definizione Link dentro la griglia. Classe di supporto per GridStaticObj.
|
|
*/
|
|
class Link
|
|
{
|
|
public:
|
|
/// Costruttore di default
|
|
inline Link(){};
|
|
/// Costruttore con inizializzatori
|
|
inline Link(ObjPtr nt, const int ni ){
|
|
assert(ni>=0);
|
|
t = nt;
|
|
i = ni;
|
|
};
|
|
|
|
|
|
inline bool operator < ( const Link & l ) const{ return i < l.i; }
|
|
inline bool operator <= ( const Link & l ) const{ return i <= l.i; }
|
|
inline bool operator > ( const Link & l ) const{ return i > l.i; }
|
|
inline bool operator >= ( const Link & l ) const{ return i >= l.i; }
|
|
inline bool operator == ( const Link & l ) const{ return i == l.i; }
|
|
inline bool operator != ( const Link & l ) const{ return i != l.i; }
|
|
|
|
inline ObjPtr & Elem() {
|
|
return t;
|
|
}
|
|
|
|
ObjType &operator *(){return *(t);}
|
|
|
|
inline int & Index() {
|
|
return i;
|
|
}
|
|
|
|
private:
|
|
/// Puntatore all'elemento T
|
|
ObjPtr t;
|
|
/// Indirizzo del voxel dentro la griglia
|
|
int i;
|
|
|
|
|
|
};//end class Link
|
|
|
|
typedef Link* Cell;
|
|
typedef Cell CellIterator;
|
|
|
|
std::vector<Link> links; /// Insieme di tutti i links
|
|
|
|
std::vector<Cell> grid; /// Griglia vera e propria
|
|
|
|
|
|
|
|
bool Empty() const {return links.empty();}
|
|
|
|
/// Date le coordinate di un grid point (corner minx,miy,minz) ritorna le celle che condividono
|
|
/// l'edge cell che parte dal grid point in direzione axis
|
|
inline void Grid( Point3i p, const int axis,
|
|
std::vector<Cell*> & cl)
|
|
{
|
|
#ifndef NDEBUG
|
|
if ( p[0]<0 || p[0] > BT::siz[0] ||
|
|
p[1]<0 || p[1]> BT::siz[1] ||
|
|
p[2]<0 || p[2]> BT::siz[2] )
|
|
assert(0);
|
|
//return NULL;
|
|
else
|
|
#endif
|
|
assert(((unsigned int) p[0]+BT::siz[0]*p[1]+BT::siz[1]*p[2])<grid.size());
|
|
|
|
int axis0 = (axis+1)%3;
|
|
int axis1 = (axis+2)%3;
|
|
int i,j,x,y;
|
|
x = p[axis0];
|
|
y = p[axis1];
|
|
for(i = std::max(x-1,0); i <= std::min( x,BT::siz[axis0]-1);++i)
|
|
for(j = std::max(y-1,0); j <= std::min( y,this->siz[axis1]-1);++j){
|
|
p[axis0]=i;
|
|
p[axis1]=j;
|
|
cl.push_back(Grid(p[0]+BT::siz[0]*(p[1]+BT::siz[1]*p[2])));
|
|
}
|
|
}
|
|
|
|
|
|
////////////////
|
|
// Official access functions
|
|
////////////////
|
|
/// BY CELL
|
|
Cell* Grid(const int i) {
|
|
return &grid[i];
|
|
}
|
|
|
|
void Grid( const Cell* g, Cell & first, Cell & last )
|
|
{
|
|
first = *g;
|
|
last = *(g+1);
|
|
}
|
|
|
|
/// BY INTEGER COORDS
|
|
inline Cell* Grid( const int x, const int y, const int z )
|
|
{
|
|
assert(!( x<0 || x>=BT::siz[0] || y<0 || y>=BT::siz[1] || z<0 || z>=BT::siz[2] ));
|
|
assert(grid.size()>0);
|
|
return &*grid.begin() + ( x+BT::siz[0]*(y+BT::siz[1]*z) );
|
|
}
|
|
|
|
inline Cell* Grid( const Point3i &pi)
|
|
{
|
|
return Grid(pi[0],pi[1],pi[2]);
|
|
}
|
|
|
|
void Grid( const int x, const int y, const int z, Cell & first, Cell & last )
|
|
{
|
|
Cell* g = Grid(x,y,z);
|
|
first = *g;
|
|
last = *(g+1);
|
|
}
|
|
|
|
void Grid( const Point3<ScalarType> & p, Cell & first, Cell & last )
|
|
{
|
|
Cell* g = Grid(GridP(p));
|
|
|
|
first = *g;
|
|
last = *(g+1);
|
|
}
|
|
|
|
|
|
/// Set the bounding box of the grid
|
|
///We need some extra space for numerical precision.
|
|
template <class Box3Type>
|
|
void SetBBox( const Box3Type & b )
|
|
{
|
|
this->bbox.Import( b );
|
|
ScalarType t = this->bbox.Diag()/100.0;
|
|
if(t == 0) t = ScalarType(1e-20); // <--- Some doubts on this (Cigno 5/1/04)
|
|
this->bbox.Offset(t);
|
|
this->dim = this->bbox.max - this->bbox.min;
|
|
}
|
|
|
|
|
|
|
|
// void ShowStats(FILE *fp)
|
|
// {
|
|
// // Conto le entry
|
|
// //int nentry = 0;
|
|
// //Hist H;
|
|
// //H.SetRange(0,1000,1000);
|
|
// //int pg;
|
|
// //for(pg=0;pg<grid.size()-1;++pg)
|
|
// // if( grid[pg]!=grid[pg+1] )
|
|
// // {
|
|
// // ++nentry;
|
|
// // H.Add(grid[pg+1]-grid[pg]);
|
|
// // }
|
|
|
|
// // fprintf(fp,"Uniform Grid: %d x %d x %d (%d voxels), %.1f%% full, %d links \nNon empty Cell Occupancy Distribution Avg: %f (%4.0f %4.0f %4.0f) \n",
|
|
// // siz[0],siz[1],siz[2],grid.size()-1,
|
|
// // double(nentry)*100.0/(grid.size()-1),links.size(),H.Avg(),H.Percentile(.25),H.Percentile(.5),H.Percentile(.75)
|
|
// //
|
|
// //);
|
|
// }
|
|
|
|
template <class OBJITER>
|
|
inline void Set(const OBJITER & _oBegin, const OBJITER & _oEnd, int _size=0)
|
|
{
|
|
Box3<FLT> _bbox;
|
|
Box3<FLT> b;
|
|
for(OBJITER i = _oBegin; i!= _oEnd; ++i)
|
|
{
|
|
(*i).GetBBox(b);
|
|
_bbox.Add(b);
|
|
}
|
|
if(_size ==0)
|
|
_size=(int)std::distance<OBJITER>(_oBegin,_oEnd);
|
|
|
|
///inflate the bb calculated
|
|
ScalarType infl=_bbox.Diag()/_size;
|
|
_bbox.min-=vcg::Point3<FLT>(infl,infl,infl);
|
|
_bbox.max+=vcg::Point3<FLT>(infl,infl,infl);
|
|
|
|
Set(_oBegin,_oEnd,_bbox,_size);
|
|
}
|
|
|
|
|
|
|
|
// This function automatically compute a reasonable size for the uniform grid providing the side (radius) of the cell
|
|
//
|
|
// Note that the bbox must be already 'inflated' so to be sure that no object will fall on the border of the grid.
|
|
|
|
template <class OBJITER>
|
|
inline void SetWithRadius(const OBJITER & _oBegin, const OBJITER & _oEnd, FLT _cellRadius)
|
|
{
|
|
Box3<FLT> _bbox;
|
|
Box3<FLT> b;
|
|
for(OBJITER i = _oBegin; i!= _oEnd; ++i)
|
|
{
|
|
(*i).GetBBox(b);
|
|
_bbox.Add(b);
|
|
}
|
|
|
|
_bbox.min-=vcg::Point3<FLT>(_cellRadius,_cellRadius,_cellRadius);
|
|
_bbox.max+=vcg::Point3<FLT>(_cellRadius,_cellRadius,_cellRadius);
|
|
|
|
Point3i _siz;
|
|
Point3<FLT> _dim = _bbox.max - _bbox.min;
|
|
_dim/=_cellRadius;
|
|
assert(_dim[0]>0 && _dim[1]>0 && _dim[2]>0 );
|
|
_siz[0] = (int)ceil(_dim[0]);
|
|
_siz[1] = (int)ceil(_dim[1]);
|
|
_siz[2] = (int)ceil(_dim[2]);
|
|
|
|
Set(_oBegin,_oEnd, _bbox,_siz);
|
|
}
|
|
|
|
|
|
// This function automatically compute a reasonable size for the uniform grid such that the number of cells is
|
|
// the same of the nubmer of elements to be inserted in the grid.
|
|
//
|
|
// Note that the bbox must be already 'inflated' so to be sure that no object will fall on the border of the grid.
|
|
|
|
template <class OBJITER>
|
|
inline void Set(const OBJITER & _oBegin, const OBJITER & _oEnd, const Box3x &_bbox, int _size=0)
|
|
{
|
|
if(_size==0)
|
|
_size=(int)std::distance<OBJITER>(_oBegin,_oEnd);
|
|
Point3<FLT> _dim = _bbox.max - _bbox.min;
|
|
Point3i _siz;
|
|
BestDim( _size, _dim, _siz );
|
|
|
|
Set(_oBegin,_oEnd,_bbox,_siz);
|
|
}
|
|
|
|
|
|
// This is the REAL LOW LEVEL function
|
|
|
|
template <class OBJITER>
|
|
inline void Set(const OBJITER & _oBegin, const OBJITER & _oEnd, const Box3x &_bbox, Point3i _siz)
|
|
{
|
|
OBJITER i;
|
|
|
|
this->bbox=_bbox;
|
|
this->siz=_siz;
|
|
|
|
// find voxel size starting from the provided bbox and grid size.
|
|
|
|
this->dim = this->bbox.max - this->bbox.min;
|
|
this->voxel[0] = this->dim[0]/this->siz[0];
|
|
this->voxel[1] = this->dim[1]/this->siz[1];
|
|
this->voxel[2] = this->dim[2]/this->siz[2];
|
|
|
|
// Allocate the grid (add one more for the final sentinel)
|
|
grid.resize( this->siz[0]*this->siz[1]*this->siz[2]+1 );
|
|
|
|
// Insert all the objects into the grid
|
|
links.clear();
|
|
for(i=_oBegin; i!=_oEnd; ++i)
|
|
{
|
|
Box3x bb; // Boundig box del tetraedro corrente
|
|
(*i).GetBBox(bb);
|
|
bb.Intersect(this->bbox);
|
|
if(! bb.IsNull() )
|
|
{
|
|
|
|
Box3i ib; // Boundig box in voxels
|
|
this->BoxToIBox( bb,ib );
|
|
int x,y,z;
|
|
for(z=ib.min[2];z<=ib.max[2];++z)
|
|
{
|
|
int bz = z*this->siz[1];
|
|
for(y=ib.min[1];y<=ib.max[1];++y)
|
|
{
|
|
int by = (y+bz)*this->siz[0];
|
|
for(x=ib.min[0];x<=ib.max[0];++x)
|
|
// Inserire calcolo cella corrente
|
|
// if( pt->Intersect( ... )
|
|
links.push_back( Link(&(*i),by+x) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Push della sentinella
|
|
/*links.push_back( Link((typename ContainerType::iterator)NULL,
|
|
(grid.size()-1)));*/
|
|
|
|
links.push_back( Link( NULL, int(grid.size())-1) );
|
|
|
|
// Ordinamento dei links
|
|
sort( links.begin(), links.end() );
|
|
|
|
// Creazione puntatori ai links
|
|
typename std::vector<Link>::iterator pl;
|
|
unsigned int pg;
|
|
pl = links.begin();
|
|
for(pg=0;pg<grid.size();++pg)
|
|
{
|
|
assert(pl!=links.end());
|
|
grid[pg] = &*pl;
|
|
while( (int)pg == pl->Index() ) // Trovato inizio
|
|
{
|
|
++pl; // Ricerca prossimo blocco
|
|
if(pl==links.end())
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int MemUsed()
|
|
{
|
|
return sizeof(GridStaticPtr)+ sizeof(Link)*links.size() +
|
|
sizeof(Cell) * grid.size();
|
|
}
|
|
|
|
template <class OBJPOINTDISTFUNCTOR, class OBJMARKER>
|
|
ObjPtr GetClosest(OBJPOINTDISTFUNCTOR & _getPointDistance, OBJMARKER & _marker,
|
|
const typename OBJPOINTDISTFUNCTOR::QueryType & _p, const ScalarType & _maxDist,ScalarType & _minDist, CoordType & _closestPt)
|
|
{
|
|
return (vcg::GridClosest<GridPtrType,OBJPOINTDISTFUNCTOR,OBJMARKER>(*this,_getPointDistance,_marker, _p,_maxDist,_minDist,_closestPt));
|
|
}
|
|
|
|
|
|
template <class OBJPOINTDISTFUNCTOR, class OBJMARKER, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
|
|
unsigned int GetKClosest(OBJPOINTDISTFUNCTOR & _getPointDistance,OBJMARKER & _marker,
|
|
const unsigned int _k, const CoordType & _p, const ScalarType & _maxDist,OBJPTRCONTAINER & _objectPtrs,
|
|
DISTCONTAINER & _distances, POINTCONTAINER & _points)
|
|
{
|
|
return (vcg::GridGetKClosest<GridPtrType,
|
|
OBJPOINTDISTFUNCTOR,OBJMARKER,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>(*this,_getPointDistance,_marker,_k,_p,_maxDist,_objectPtrs,_distances,_points));
|
|
}
|
|
|
|
template <class OBJPOINTDISTFUNCTOR, class OBJMARKER, class OBJPTRCONTAINER, class DISTCONTAINER, class POINTCONTAINER>
|
|
unsigned int GetInSphere(OBJPOINTDISTFUNCTOR & _getPointDistance,
|
|
OBJMARKER & _marker,
|
|
const CoordType & _p,
|
|
const ScalarType & _r,
|
|
OBJPTRCONTAINER & _objectPtrs,
|
|
DISTCONTAINER & _distances,
|
|
POINTCONTAINER & _points)
|
|
{
|
|
return(vcg::GridGetInSphere<GridPtrType,
|
|
OBJPOINTDISTFUNCTOR,OBJMARKER,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>
|
|
(*this,_getPointDistance,_marker,_p,_r,_objectPtrs,_distances,_points));
|
|
}
|
|
|
|
template <class OBJMARKER, class OBJPTRCONTAINER>
|
|
unsigned int GetInBox(OBJMARKER & _marker,
|
|
const vcg::Box3<ScalarType> _bbox,
|
|
OBJPTRCONTAINER & _objectPtrs)
|
|
{
|
|
return(vcg::GridGetInBox<GridPtrType,OBJMARKER,OBJPTRCONTAINER>
|
|
(*this,_marker,_bbox,_objectPtrs));
|
|
}
|
|
|
|
template <class OBJRAYISECTFUNCTOR, class OBJMARKER>
|
|
ObjPtr DoRay(OBJRAYISECTFUNCTOR & _rayIntersector, OBJMARKER & _marker, const Ray3<ScalarType> & _ray, const ScalarType & _maxDist, ScalarType & _t)
|
|
{
|
|
return(vcg::GridDoRay<GridPtrType,OBJRAYISECTFUNCTOR,OBJMARKER>(*this,_rayIntersector,_marker,_ray,_maxDist,_t));
|
|
}
|
|
|
|
/* If the grid has a cubic voxel of side <radius> this function
|
|
process all couple of elementes in neighbouring cells.
|
|
GATHERFUNCTOR needs to expose this method:
|
|
bool operator()(OBJTYPE *v1, OBJTYPE *v2);
|
|
which is then called ONCE per unordered pair v1,v2.
|
|
example:
|
|
|
|
struct GFunctor {
|
|
double radius2, iradius2;
|
|
GFunctor(double radius) { radius2 = radius*radius; iradius2 = 1/radius2; }
|
|
|
|
bool operator()(CVertex *v1, CVertex *v2) {
|
|
Point3d &p = v1->P();
|
|
Point3d &q = v2->P();
|
|
double dist2 = (p-q).SquaredNorm();
|
|
if(dist2 < radius2) {
|
|
double w = exp(dist2*iradius2);
|
|
//do something
|
|
}
|
|
}
|
|
}; */
|
|
|
|
template <class GATHERFUNCTOR>
|
|
void Gather(GATHERFUNCTOR gfunctor) {
|
|
static int corner[8*3] = { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
|
|
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 };
|
|
|
|
static int diagonals[14*2] = { 0, 0,
|
|
0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7,
|
|
2, 3, 1, 3, 1, 2,
|
|
1, 4, 2, 5, 3, 6 };
|
|
|
|
Cell ostart, oend, dstart, dend;
|
|
for(int z = 0; z < this->siz[2]; z++) {
|
|
for(int y = 0; y < this->siz[1]; y++) {
|
|
for(int x = 0; x < this->siz[0]; x++) {
|
|
|
|
Grid(x, y, z, ostart, oend);
|
|
|
|
for(Cell c = ostart; c != oend; c++)
|
|
for(Cell s = c+1; s != oend; s++)
|
|
gfunctor(c->Elem(), s->Elem());
|
|
|
|
for(int d = 2; d < 28; d += 2) { //skipping self
|
|
int *cs = corner + 3*diagonals[d];
|
|
int *ce = corner + 3*diagonals[d+1];
|
|
if((x + cs[0] < this->siz[0]) && (y + cs[1] < this->siz[1]) && (z + cs[2] < this->siz[2]) &&
|
|
(x + ce[0] < this->siz[0]) && (y + ce[1] < this->siz[1]) && (z + ce[2] < this->siz[2])) {
|
|
|
|
Grid(x+cs[0], y+cs[1], z+cs[2], ostart, oend);
|
|
Grid(x+ce[0], y+ce[1], z+ce[2], dstart, dend);
|
|
|
|
for(Cell c = ostart; c != oend; c++)
|
|
for(Cell s = dstart; s != dend; s++)
|
|
gfunctor(c->Elem(), s->Elem());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}; //end class GridStaticPtr
|
|
|
|
} // end namespace
|
|
|
|
#endif
|