1 #include <stdio.h>
2 #include <vcg/complex/complex.h>
3 #include <vcg/complex/algorithms/update/flag.h>
4 #include <vcg/complex/algorithms/update/curvature.h>
5 #include <vcg/complex/algorithms/update/quality.h>
6 #include <vcg/complex/algorithms/update/topology.h>
7 #include <vcg/complex/algorithms/clean.h>
8 #include <vcg/complex/algorithms/stat.h>
9 #include <vcg/math/histogram.h>
10 #include <vcg/complex/algorithms/smooth.h>
11 #include <vcg/complex/algorithms/clustering.h>
12
13 #include <meshlabplugins/filter_mls/apss.h>
14 #include <meshlabplugins/filter_mls/implicits.h>
15
16 using namespace vcg;
17 using namespace std;
18 using namespace GaelMls;
19
20 //class for a multiscale feature based on mean curvature: curvature is computed at differents levels of smoothness
21 template<class MESH_TYPE, int dim>
22 class SmoothCurvatureFeature
23 {
24 public:
25
26 class Parameters
27 {
28 public:
29
30 enum CurvatureType {GAUSSIAN, MEAN, ABSOLUTE};
31
32 struct Item
33 {
34 CurvatureType type;
35 int scale;
36 float lower_bound, upper_bound;
37
38 Item(CurvatureType _type, int _scale, float _lower_bound = 0.0f, float _upper_bound = 1.0f):
typeItem39 type(_type),scale(_scale),lower_bound(_lower_bound),upper_bound(_upper_bound){}
40 };
41
42 vector<Item> featureDesc;
43
44 bool add(CurvatureType cType, int smoothStep, float lower_bound = 0.0f, float upper_bound = 1.0f){
45
46 assert(smoothStep>=0 & featureDesc.size()<dim & lower_bound>=0.0f & upper_bound<=1.0f & lower_bound<upper_bound);
47 featureDesc.push_back(Item(cType, smoothStep, lower_bound, upper_bound));
48 return true;
49 }
50 };
51
52 typedef MESH_TYPE MeshType;
53 typedef SmoothCurvatureFeature<MeshType,dim> FeatureType;
54 typedef typename FeatureType::Parameters ParamType;
55 typedef typename MeshType::ScalarType ScalarType;
56 typedef typename MeshType::VertexType VertexType;
57 typedef typename MeshType::VertexIterator VertexIterator;
58 typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
59
60 Point3<ScalarType> pos;
61 Point3<ScalarType> normal;
62 float description[dim];
63
64 SmoothCurvatureFeature();
65 SmoothCurvatureFeature(VertexType& v);
66 static char* getName();
67 static int getRequirements();
68 static float getNullValue();
69 static bool isNullValue(float);
70 static int getFeatureDimension();
71 static void SetupParameters(ParamType& param );
72 static bool HasBeenComputed(MeshType& m);
73 static bool ComputeFeature(MeshType&, ParamType& param, CallBackPos *cb=NULL);
74 static bool Subset(int, MeshType&, vector<FeatureType*>&, int, CallBackPos *cb=NULL);
75 static bool CheckPersistency(FeatureType f);
76
77 private:
78 static MESH_TYPE* CreateSamplingMesh();
79 static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
80 static pair<float,float> FindMinMax(vector<FeatureType*>& vec, int scale);
81 static void SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted);
82 static int SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius);
83 static void PreCleaning(MeshType& m);
84 };
85
86 template<class MESH_TYPE, int dim>
SmoothCurvatureFeature()87 inline SmoothCurvatureFeature<MESH_TYPE,dim>::SmoothCurvatureFeature(){}
88
89 template<class MESH_TYPE, int dim>
SmoothCurvatureFeature(VertexType & v)90 inline SmoothCurvatureFeature<MESH_TYPE,dim>::SmoothCurvatureFeature(VertexType& v):pos(v.P()),normal(v.N())
91 {
92 normal.Normalize();
93 for(int i=0; i<dim; i++) SmoothCurvatureFeature::getNullValue();
94 }
95
getName()96 template<class MESH_TYPE, int dim> inline char* SmoothCurvatureFeature<MESH_TYPE,dim>::getName()
97 {
98 return "SmoothCurvatureFeature";
99 }
100
101 /* Provides needed attribute to compute feature. A detailed list follows:
102 MM_FACEFACETOPO required by RemoveNonManifoldVertex and RemoveNonManifoldFace
103 MM_FACEFLAGBORDER required by RemoveNonManifoldFace
104 MM_VERTCURV required by curvature computation
105 MM_VERTQUALITY required by curvature and histogram computation
106 */
getRequirements()107 template<class MESH_TYPE, int dim> inline int SmoothCurvatureFeature<MESH_TYPE,dim>::getRequirements()
108 {
109 return (MeshModel::MM_VERTCURV |
110 MeshModel::MM_VERTQUALITY |
111 MeshModel::MM_FACEFACETOPO |
112 MeshModel::MM_FACEFLAGBORDER);
113 }
114
isNullValue(float val)115 template<class MESH_TYPE, int dim> inline bool SmoothCurvatureFeature<MESH_TYPE,dim>::isNullValue(float val)
116 {
117 return ( ((val==FeatureType::getNullValue()) | (math::IsNAN(val))) );
118 }
119
getNullValue()120 template<class MESH_TYPE, int dim> inline float SmoothCurvatureFeature<MESH_TYPE,dim>::getNullValue()
121 {
122 return -std::numeric_limits<float>::max();
123 }
124
getFeatureDimension()125 template<class MESH_TYPE, int dim> inline int SmoothCurvatureFeature<MESH_TYPE,dim>::getFeatureDimension()
126 {
127 return dim;
128 }
129
130 //check persistence beetween scales: return true if description is valid for all scales, false otherwise
CheckPersistency(FeatureType f)131 template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::CheckPersistency(FeatureType f)
132 {
133 for(int i = 0; i<FeatureType::getFeatureDimension(); i++)
134 {
135 if( FeatureType::isNullValue(f.description[i]) ) return false;
136 }
137 return true;
138 }
139
140 //parameters must be ordered according to smooth iterations
SetupParameters(ParamType & param)141 template<class MESH_TYPE, int dim> void SmoothCurvatureFeature<MESH_TYPE,dim>::SetupParameters(ParamType& param)
142 {
143 param.add(Parameters::GAUSSIAN, 5, 0.4f, 0.9f);
144 param.add(Parameters::MEAN, 5, 0.4f, 0.9f);
145 param.add(Parameters::GAUSSIAN, 10, 0.4f, 0.9f);
146 param.add(Parameters::MEAN, 10, 0.4f, 0.9f);
147 param.add(Parameters::GAUSSIAN, 15, 0.4f, 0.9f);
148 param.add(Parameters::MEAN, 15, 0.4f, 0.9f);
149 assert(int(param.featureDesc.size())==getFeatureDimension());
150 }
151
PreCleaning(MeshType & m)152 template<class MESH_TYPE, int dim> void SmoothCurvatureFeature<MESH_TYPE,dim>::PreCleaning(MeshType& m)
153 {
154 tri::Clean<MeshType>::RemoveZeroAreaFace(m);
155 tri::Clean<MeshType>::RemoveDuplicateFace(m);
156 tri::Clean<MeshType>::RemoveDuplicateVertex(m);
157 tri::Clean<MeshType>::RemoveNonManifoldFace(m);
158 //tri::Clean<MeshType>::RemoveNonManifoldVertex(m);
159 tri::Clean<MeshType>::RemoveUnreferencedVertex(m);
160 tri::Allocator<MeshType>::CompactVertexVector(m);
161 }
162
HasBeenComputed(MeshType & m)163 template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::HasBeenComputed(MeshType &m)
164 {
165 //checks if the attribute exist
166 return tri::HasPerVertexAttribute(m,std::string(FeatureType::getName()));
167 }
168
ComputeFeature(MeshType & m,ParamType & param,CallBackPos * cb)169 template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::ComputeFeature(MeshType &m, ParamType& param, CallBackPos *cb)
170 {
171 //variables needed for progress bar callback
172 float progBar = 0.0f;
173 float offset = 100.0f/((FeatureType::getFeatureDimension()+2)*m.VertexNumber());
174 if(cb) cb(0,"Computing features...");
175
176 //allocates a custom per vertex attribute in which we can store pointers to features in the heap.
177 PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m, true);
178 if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
179
180 //clear the mesh to avoid wrong values during curvature computations
181 PreCleaning(m);
182
183 //copy vertex coords of the mesh before smoothing
184 vector<Point3<ScalarType> > oldVertCoords(m.VertexNumber());
185 for(unsigned int i = 0; i<m.vert.size(); ++i){
186 oldVertCoords[i] = m.vert[i].P();
187
188 if(cb){ progBar+=offset; cb((int)progBar,"Computing features..."); }
189 }
190
191 assert(int(oldVertCoords.size())==m.VertexNumber());
192
193 //creates a feature for each vertex
194 for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) fh[vi] = FeatureType(*vi);
195
196 //loop trough scale levels
197 int smooth_step = 0, smooth_accum = 0;
198 for (unsigned int i = 0; i<FeatureType::getFeatureDimension(); i++, smooth_accum+=smooth_step)
199 {
200 smooth_step = param.featureDesc[i].scale - smooth_accum;
201
202 tri::Smooth<MeshType>::VertexCoordLaplacian(m, smooth_step);//smooth mesh
203 tri::UpdateCurvature<MeshType>::MeanAndGaussian(m); //compute curvature
204 //copy curvature values in the quality attributes; then build the histogram and take lower and upper bounds.
205 switch(param.featureDesc[i].type){
206 case Parameters::GAUSSIAN:{
207 tri::UpdateQuality<MeshType>::VertexFromGaussianCurvature(m);
208 break;
209 }
210 case Parameters::MEAN:{
211 tri::UpdateQuality<MeshType>::VertexFromMeanCurvature(m);
212 break;
213 }
214 case Parameters::ABSOLUTE:{
215 tri::UpdateQuality<MeshType>::VertexFromAbsoluteCurvature(m);
216 break;
217 }
218 default: assert(0);
219 }
220 Histogram<ScalarType> hist = Histogram<ScalarType>();
221 tri::Stat<MeshType>::ComputePerVertexQualityHistogram(m, hist);
222 float vmin = hist.Percentile(param.featureDesc[i].lower_bound); float vmax = hist.Percentile(param.featureDesc[i].upper_bound);
223
224 //If curvature is beetween bounds and vertex is not a boundary, curvature is stored in the feature, otherwise the feature is set to an empty value.
225 for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
226 {
227 if( (!(*vi).IsB()) & ((*vi).Q()<=vmin) | ((*vi).Q()>=vmax) & (!FeatureType::isNullValue((*vi).Q())) )
228 fh[vi].description[i] = (*vi).Q();
229 else
230 fh[vi].description[i] = FeatureType::getNullValue();
231
232 if(cb){ progBar+=offset; cb((int)progBar,"Computing features..."); }
233 }
234 }
235 //restore old coords
236 for(unsigned int i = 0; i<m.vert.size(); ++i){
237 m.vert[i].P() = oldVertCoords[i];
238
239 if(cb){ progBar+=offset; cb((int)progBar,"Computing features..."); }
240 }
241
242 return true;
243 }
244
245 template<class MESH_TYPE, int dim>
CreateSamplingMesh()246 MESH_TYPE* SmoothCurvatureFeature<MESH_TYPE,dim>::CreateSamplingMesh()
247 {
248 MeshType* m = new MeshType();
249 PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(*m, true);
250 if(!tri::Allocator<MeshType>::IsValidHandle(*m,fh)){
251 if(m) delete m;
252 return NULL;
253 }
254 return m;
255 }
256
257 template<class MESH_TYPE, int dim>
SetupSamplingStructures(MeshType & m,typename MeshType::template PerVertexAttributeHandle<FeatureType> & fh,MeshType * samplingMesh,vector<FeatureType * > * vecFeatures)258 int SmoothCurvatureFeature<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
259 {
260 int countFeatures = 0;
261 PVAttributeHandle pmfh;
262 if(samplingMesh){
263 pmfh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(*samplingMesh);
264 if(!tri::Allocator<MeshType>::IsValidHandle(*samplingMesh,pmfh)) return 0;
265 }
266
267 //fill the vector with all persistent features.
268 for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi){
269 //check persistence beetween scales: if feature is persistent, add a pointer in vecFeatures
270 if( FeatureType::CheckPersistency(fh[vi])){
271 countFeatures++; //increment counter of valid features
272 if(vecFeatures) vecFeatures->push_back(&(fh[vi]));
273 if(samplingMesh){
274 tri::Allocator<MeshType>::AddVertices(*samplingMesh, 1);
275 samplingMesh->vert.back().ImportLocal(*vi);
276 pmfh[samplingMesh->vert.back()] = fh[vi];
277 }
278 }
279 }
280
281 return countFeatures;
282 }
283
284 template<class MESH_TYPE, int dim>
Subset(int k,MeshType & m,vector<FeatureType * > & vecSubset,int sampType,CallBackPos * cb)285 bool SmoothCurvatureFeature<MESH_TYPE,dim>::Subset(int k, MeshType &m, vector<FeatureType*> &vecSubset, int sampType, CallBackPos *cb)
286 {
287 //variables needed for progress bar callback
288 float progBar = 0.0f;
289 float offset = 100.0f/(m.VertexNumber() + k);
290
291 //if attribute doesn't exist, return; else we can get a handle to the attribute
292 PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m);
293 if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
294
295 //create a vector to hold valid features that later will be sampled
296 vector<FeatureType*>* vecFeatures = NULL;
297 MeshType* poissonMesh = NULL;
298 if(sampType==0) vecFeatures = new vector<FeatureType*>();
299 else poissonMesh = CreateSamplingMesh();
300
301 //fill the vector with all persistent features.
302 int countFeatures = SetupSamplingStructures(m, fh, poissonMesh, vecFeatures);
303 if(countFeatures<k) k = countFeatures; //we can't extract more of what we got!
304
305 //perform different kinds of sampling
306 FeatureType** sampler = NULL;
307 switch(sampType){
308 case 0:{ //uniform sampling: uses vecFeatures
309 sampler = FeatureAlignment<MeshType, FeatureType>::FeatureUniform(*vecFeatures, &k);
310 break;
311 }
312 case 1:{ //poisson disk sampling: uses poissonMesh
313 sampler = FeatureAlignment<MeshType, FeatureType>::FeaturePoisson(*poissonMesh, &k);
314 break;
315 }
316 default: assert(0);
317 }
318
319 //store features into the returned vector
320 for(int i=0; i<k; ++i){
321 vecSubset.push_back(sampler[i]);
322 if(cb){ progBar+=offset; cb(int(progBar),"Extracting features..."); }
323 }
324
325 if(vecFeatures) delete vecFeatures; //clear useless data
326 if(poissonMesh) delete poissonMesh;
327 if(sampler) delete[] sampler;
328
329 return true;
330
331 ////UNCOMMENT FOLLOW TO GET A RARE FEATURE SELECTION////
332 /*
333 int histSize = 10000; //number of bins of the histogram
334 vector<FeatureType*>* sorted = new vector<FeatureType*>(); //vector that holds features sorted by bin cont
335
336 //compute min val e max val between all features; min and max are needed to bound the histogram
337 pair<float,float> minmax = FindMinMax(*vecFeatures, 0);
338
339 //fill multimap with features sorted by bin count
340 SortByBinCount(*vecFeatures, minmax.first, minmax.second, histSize, *sorted);
341
342 //select the first k entries from mulptimap, and put them into vecSubset
343 typename vector<FeatureType*>::iterator it = sorted->begin();
344 ///UNCOMMENT THIS LOOP FOR A SORTED SELECTION
345 for(int i=0; i<k & it!=sorted->end(); i++, it++)
346 {
347 (*it)->selected = true; //mark features as selected
348 vecSubset.push_back(*it);
349 }
350 ///UNCOMMENT THIS LOOP FOR A EQUALIZED SELECTION
351 int off = int(sorted->size()/k);
352 for(it = sorted->begin(); it<sorted->end(); it=it+off)
353 {
354 (*it)->selected = true; //mark features as selected
355 vecSubset.push_back(*it);
356 }
357
358 //delete vector and set pointer to NULL for safety
359 if(vecFeatures) delete vecFeatures; //clear useless data
360 sorted->clear();
361 delete sorted;
362 sorted = NULL;
363 return true;
364 */
365 }
366
367 //scan the vector of features and return a pair containig the min and max description values
FindMinMax(vector<FeatureType * > & vec,int scale)368 template<class MESH_TYPE, int dim> pair<float,float> SmoothCurvatureFeature<MESH_TYPE,dim>::FindMinMax(vector<FeatureType*>& vec, int scale)
369 {
370 assert(scale>=0 && scale<FeatureType::GetFeatureDimension());
371
372 typename vector<FeatureType*>::iterator vi;
373 pair<float,float> minmax = make_pair(numeric_limits<float>::max(),-numeric_limits<float>::max());
374 for(vi = vec.begin(); vi!=vec.end(); ++vi)
375 {
376 if( !FeatureType::isNullValue((*vi)->description[scale])) //invalid values are discarded
377 {
378 if( (*vi)->description[scale] < minmax.first) minmax.first=(*vi)->description[scale];
379 if( (*vi)->description[scale] > minmax.second ) minmax.second=(*vi)->description[scale];
380 }
381 }
382 return minmax;
383 }
384
385 //fill a vector that holds features pointers sorted by bin count; i.e first all the very infrequent features and last all the very frequent ones.
SortByBinCount(vector<FeatureType * > & vecFeatures,float min,float max,int histSize,vector<FeatureType * > & sorted)386 template<class MESH_TYPE, int dim> void SmoothCurvatureFeature<MESH_TYPE,dim>::SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted)
387 {
388 //vector to hold bins ranges
389 vector<float>* bins = new vector<float>(histSize, 0);
390 //vector to count content of each bin
391 vector<int> *hToC = new vector<int>(histSize, 0);
392 //multimap to keep track of features for each bin
393 multimap<int, FeatureType* >* hToF = new multimap<int, FeatureType* >();
394 //multimap to store pairs of (count, FeatureType*). multimap is naturally sorted by count.
395 multimap<int, FeatureType* >* cToF = new multimap<int, FeatureType* >();
396
397 //offset beetween min and max is divided into histSize uniform bins
398 for(unsigned int i = 0; i<bins->size(); i++) bins->at(i) = min + i*(max-min)/float(histSize);
399
400 //build histogram, hToF and hToC; all with just one features scan
401 typename multimap<int, FeatureType* >::iterator hfIt = hToF->begin();
402 typename vector<FeatureType*>::iterator vi;
403 for(vi = vecFeatures.begin(); vi!=vecFeatures.end(); ++vi)
404 {
405 float val = (*vi)->description[0];
406 // lower_bound returns an iterator pointing to the first element "not less than" val, or end() if every element is less than val.
407 typename vector<float>::iterator it = lower_bound(bins->begin(),bins->end(),val);
408 //if val is out of range, skip iteration and take in account next val
409 if(it==bins->begin() || it==bins->end()) continue;
410 //determine in which bin got to insert val
411 int binId = (it - bins->begin())-1;
412 assert(binId>=0);
413 assert (bins->at(binId) < val);
414 assert (val <= bins->at(binId+1));
415
416 //increment bin count
417 hToC->at(binId)++;
418 //remember in which bin has been inserted this feature
419 hfIt = hToF->insert(hfIt,make_pair(binId, (*vi)));
420 }
421
422 //delete bins and set pointer to NULL for safety
423 bins->clear();
424 delete bins;
425 bins = NULL;
426
427 //for all bin index insert in the multimap an entry with key bin count and value a feature. Entries are naturally
428 //sorted by key in the multimap
429 typename multimap<int, FeatureType* >::iterator cfIt = cToF->begin();
430 pair< typename multimap<int, FeatureType* >::iterator, typename multimap<int, FeatureType* >::iterator> range;
431 for(unsigned int i=0; i<hToC->size(); i++)
432 {
433 //if bin count is zero, then skip; nothing to put in the multimap
434 if(hToC->at(i)!=0)
435 {
436 range = hToF->equal_range(i);
437 assert(range.first!=range.second);
438 for (; range.first!=range.second; ++range.first)
439 {
440 cfIt = cToF->insert( cfIt, make_pair(hToC->at(i), (*range.first).second) );
441 }
442 }
443 }
444
445 typename multimap<int, FeatureType* >::iterator it;
446 for(it = cToF->begin(); it != cToF->end(); it++)
447 sorted.push_back((*it).second);
448
449 assert(sorted.size()==cToF->size());
450
451 //delete all ausiliary structures and set pointers to NULL for safety
452 hToF->clear();
453 delete hToF;
454 hToF = NULL;
455 hToC->clear();
456 delete hToC;
457 hToC = NULL;
458 cToF->clear();
459 delete cToF;
460 cToF = NULL;
461 }
462
SpatialSelection(vector<FeatureType * > & container,vector<FeatureType * > & vec,int k,float radius)463 template<class MESH_TYPE, int dim> int SmoothCurvatureFeature<MESH_TYPE,dim>::SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius)
464 {
465 //variables to manage the kDTree which works on features position
466 ANNpointArray dataPts = NULL; // data points
467 ANNpoint queryPnt = NULL; // query points
468 ANNkd_tree* kdTree = NULL; // search structure
469
470 queryPnt = annAllocPt(3);
471
472 typename vector<FeatureType*>::iterator ci = container.begin();
473 while(ci != container.end() && vec.size()<k)
474 {
475 if(vec.size()==0)
476 {
477 vec.push_back(*ci);
478 (*ci)->selected = true;
479 ci++;
480 continue;
481 }
482
483 if(dataPts){ annDeallocPts(dataPts); dataPts = NULL; }
484 if(kdTree){ delete kdTree; kdTree = NULL; }
485 dataPts = annAllocPts(vec.size(), 3);
486
487 for(int i = 0; i < vec.size(); i++)
488 {
489 for (int j = 0; j < 3; j++)
490 {
491 (dataPts[i])[j] = (ANNcoord)(vec.at(i)->pos[j]);
492 }
493 }
494 //build search structure
495 kdTree = new ANNkd_tree(dataPts,vec.size(),3);
496
497 for (int j = 0; j < 3; j++)
498 {
499 queryPnt[j] = (ANNcoord)((*ci)->pos[j]);
500 }
501
502 //if there aren't features yet selected in the range distance
503 if(!kdTree->annkFRSearch(queryPnt, math::Sqr(radius), 0, NULL, NULL, 0.0))
504 {
505 vec.push_back(*ci);
506 (*ci)->selected = true;
507 }
508 ci++;
509 }
510
511 if(dataPts){ annDeallocPts(dataPts); dataPts = NULL; }
512 if(queryPnt){ annDeallocPt(queryPnt); queryPnt = NULL; }
513 if(kdTree){ delete kdTree; kdTree = NULL; }
514
515 return vec.size();
516 }
517
518 //class for a multiscale feature based on APSS curvature. Works with point clouds.
519 template<class MESH_TYPE, int dim>
520 class APSSCurvatureFeature
521 {
522 public:
523
524 class Parameters
525 {
526 public:
527
528 enum CurvatureType { MEAN, GAUSSIAN, K1, K2, APSS};
529
530 struct Item
531 {
532 public:
533 CurvatureType type;
534 float lower_bound, upper_bound, scale; //indicates how much sub sample the mesh. 1 = whole mesh, 0.5 = half mesh, etc
535
536 Item(CurvatureType _type, float _scale, float _lower_bound = 0.0f, float _upper_bound = 1.0f):
typeItem537 type(_type),scale(_scale),lower_bound(_lower_bound),upper_bound(_upper_bound){}
538 };
539
540 vector<Item> featureDesc;
541
542 bool add(CurvatureType cType, float subSampAmount, float lower_bound = 0.0f, float upper_bound = 1.0f)
543 {
544 assert(subSampAmount>=0 & subSampAmount<=1 & featureDesc.size()<dim & lower_bound>=0.0f & upper_bound<=1.0f & lower_bound<upper_bound);
545 featureDesc.push_back(Item(cType, subSampAmount, lower_bound, upper_bound));
546 return true;
547 }
548 };
549
550 typedef MESH_TYPE MeshType;
551 typedef APSSCurvatureFeature<MeshType,dim> FeatureType;
552 typedef typename FeatureType::Parameters ParamType;
553 typedef typename MeshType::ScalarType ScalarType;
554 typedef typename MeshType::VertexType VertexType;
555 typedef typename MeshType::VertexIterator VertexIterator;
556 typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
557
558 Point3<ScalarType> pos;
559 Point3<ScalarType> normal;
560 float description[dim];
561
562 APSSCurvatureFeature();
563 APSSCurvatureFeature(VertexType& v);
564 static char* getName();
565 static int getRequirements();
566 static float getNullValue();
567 static bool isNullValue(float);
568 static int getFeatureDimension();
569 static void SetupParameters(ParamType& param );
570 static bool HasBeenComputed(MeshType& m);
571 static bool ComputeFeature(MeshType&, ParamType& param, CallBackPos *cb=NULL);
572 static bool Subset(int, MeshType&, vector<FeatureType*>&, int, CallBackPos *cb=NULL);
573 static bool CheckPersistency(FeatureType f);
574
575 private:
576 static bool ComputeAPSS(MeshType& m, int type, float filterScale, int maxProjIter, float projAcc, float sphPar, bool accNorm, bool selectionOnly);
577 static MESH_TYPE* CreateSamplingMesh();
578 static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
579 static pair<float,float> FindMinMax(vector<FeatureType*>& vec, int scale);
580 static void SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted);
581 static int SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius);
582 static void PreCleaning(MeshType& m);
583 };
584
585 template<class MESH_TYPE, int dim>
APSSCurvatureFeature()586 inline APSSCurvatureFeature<MESH_TYPE,dim>::APSSCurvatureFeature(){}
587
588 template<class MESH_TYPE, int dim>
APSSCurvatureFeature(VertexType & v)589 inline APSSCurvatureFeature<MESH_TYPE,dim>::APSSCurvatureFeature(VertexType& v):pos(v.P()),normal(v.N())
590 {
591 normal.Normalize();
592 for(int i=0; i<dim; i++) APSSCurvatureFeature::getNullValue();
593 }
594
getName()595 template<class MESH_TYPE, int dim> inline char* APSSCurvatureFeature<MESH_TYPE,dim>::getName()
596 {
597 return "APSSCurvatureFeature";
598 }
599
600 /* Provides needed attribute to compute feature. A detailed list follows:
601 MM_VERTCURV required by curvature computation
602 MM_VERTQUALITY required by curvature and histogram computation
603 MM_VERTRADIUS required by APSS curvature computation
604 MM_VERTCURVDIR required by APSS curvature computation
605 */
getRequirements()606 template<class MESH_TYPE, int dim> inline int APSSCurvatureFeature<MESH_TYPE,dim>::getRequirements()
607 {
608 return (MeshModel::MM_VERTCURVDIR | MeshModel::MM_VERTQUALITY | MeshModel::MM_VERTRADIUS);
609 }
610
isNullValue(float val)611 template<class MESH_TYPE, int dim> inline bool APSSCurvatureFeature<MESH_TYPE,dim>::isNullValue(float val)
612 {
613 return ( ((val==FeatureType::getNullValue()) | (math::IsNAN(val))) );
614 }
615
getNullValue()616 template<class MESH_TYPE, int dim> inline float APSSCurvatureFeature<MESH_TYPE,dim>::getNullValue()
617 {
618 return -std::numeric_limits<float>::max();
619 }
620
getFeatureDimension()621 template<class MESH_TYPE, int dim> inline int APSSCurvatureFeature<MESH_TYPE,dim>::getFeatureDimension()
622 {
623 return dim;
624 }
625
626 //check persistence beetween scales: return true if description is valid for all scales, false otherwise
CheckPersistency(FeatureType f)627 template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::CheckPersistency(FeatureType f)
628 {
629 for(int i = 0; i<FeatureType::getFeatureDimension(); i++)
630 {
631 if( FeatureType::isNullValue(f.description[i]) ) return false;
632 }
633 return true;
634 }
635
636 //parameters must be ordered according to smooth iterations
SetupParameters(ParamType & param)637 template<class MESH_TYPE, int dim> void APSSCurvatureFeature<MESH_TYPE,dim>::SetupParameters(ParamType& param)
638 {
639 param.add(Parameters::MEAN, 1.0f, 0.3f, 0.9f);
640 param.add(Parameters::MEAN, 0.75f, 0.3f, 0.9f);
641 param.add(Parameters::MEAN, 0.5f, 0.3f, 0.9f);
642 assert(int(param.featureDesc.size())==getFeatureDimension());
643 }
644
PreCleaning(MeshType & m)645 template<class MESH_TYPE, int dim> void APSSCurvatureFeature<MESH_TYPE,dim>::PreCleaning(MeshType& m)
646 {
647 //if we are not working on point clouds, clean up faces
648 if(m.fn>0)
649 {
650 tri::Clean<MeshType>::RemoveZeroAreaFace(m);
651 tri::Clean<MeshType>::RemoveDuplicateFace(m);
652 tri::Clean<MeshType>::RemoveDuplicateVertex(m);
653 tri::Clean<MeshType>::RemoveUnreferencedVertex(m);
654 }
655 tri::Allocator<MeshType>::CompactVertexVector(m);
656 }
657
658 template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::ComputeAPSS(MeshType& m, int type = 0, float filterScale = 2.0f, int maxProjIter = 15, float projAcc = 1e-4f, float sphPar = 1.0f, bool accNorm = true, bool selectionOnly=true)
659 {
660 // create the MLS surface
661 APSS<MeshType>* mls = new APSS<MeshType>(m);
662
663 // We require a per vertex radius so as a first thing compute it
664 mls->computeVertexRaddi();
665
666 mls->setFilterScale(filterScale);
667 mls->setMaxProjectionIters(maxProjIter);
668 mls->setProjectionAccuracy(projAcc);
669 mls->setSphericalParameter(sphPar);
670 mls->setGradientHint(accNorm ? GaelMls::MLS_DERIVATIVE_ACCURATE : GaelMls::MLS_DERIVATIVE_APPROX);
671
672 uint size = m.vert.size();
673 vcg::Point3f grad;
674 vcg::Matrix33f hess;
675
676 //computes curvatures and statistics
677 for (unsigned int i = 0; i< size; i++)
678 {
679 if ( (!selectionOnly) || (m.vert[i].IsS()) )
680 {
681 Point3f p = mls->project(m.vert[i].P());
682 float c = 0;
683
684 if (type==Parameters::APSS) c = mls->approxMeanCurvature(p);
685 else
686 {
687 int errorMask;
688 grad = mls->gradient(p, &errorMask);
689 if (errorMask == MLS_OK && grad.Norm() > 1e-8)
690 {
691 hess = mls->hessian(p, &errorMask);
692 implicits::WeingartenMap<float> W(grad,hess);
693
694 m.vert[i].PD1() = W.K1Dir();
695 m.vert[i].PD2() = W.K2Dir();
696 m.vert[i].K1() = W.K1();
697 m.vert[i].K2() = W.K2();
698
699 switch(type){
700 case Parameters::MEAN: c = W.MeanCurvature(); break;
701 case Parameters::GAUSSIAN: c = W.GaussCurvature(); break;
702 case Parameters::K1: c = W.K1(); break;
703 case Parameters::K2: c = W.K2(); break;
704 default: assert(0 && "invalid curvature type");
705 }
706 }
707 assert(!math::IsNAN(c) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)");
708 }
709 m.vert[i].Q() = c;
710 }
711 }
712
713 delete mls;
714
715 return true;
716 }
717
HasBeenComputed(MeshType & m)718 template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::HasBeenComputed(MeshType &m)
719 {
720 //checks if the attribute exist
721 return tri::HasPerVertexAttribute(m,std::string(FeatureType::getName()));
722 }
723
ComputeFeature(MeshType & m,ParamType & param,CallBackPos * cb)724 template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::ComputeFeature(MeshType &m, ParamType& param, CallBackPos *cb)
725 {
726 //variables needed for progress bar callback
727 float progBar = 0.0f;
728 float offset = 100.0f/((FeatureType::getFeatureDimension())*m.VertexNumber());
729 if(cb) cb(0,"Computing features...");
730
731 //allocates a custom per vertex attribute in which we can store pointers to features in the heap.
732 PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m, true);
733 if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
734
735 //clear the mesh to avoid wrong values during curvature computations
736 PreCleaning(m);
737
738 //creates a feature for each vertex
739 for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) fh[vi] = FeatureType(*vi);
740
741 //loop trough scale levels
742 for (unsigned int i = 0; i<FeatureType::getFeatureDimension(); i++)
743 {
744 //compute the amount of verteces desidered for this scale
745 int targetSize = int(m.VertexNumber()*param.featureDesc[i].scale);
746 //set up the clustering structure and perform a logaritmic search in order to get a number
747 //of clustered vertex very close to the target number
748 tri::Clustering<MeshType, tri::NearestToCenter<MeshType> > ClusteringGrid;
749 bool done = false;
750 bool onlySelected = (i==0) ? false : true; //we subsample the whole mesh just the first time, then we subsample from the previous scale!
751 int size = 10*targetSize, errSize = int(0.02f*targetSize), inf = 0, sup = 0;
752 do{
753 ClusteringGrid.Init(m.bbox,size);
754 ClusteringGrid.AddPointSet(m,onlySelected);
755 int sel = ClusteringGrid.CountPointSet();
756 if(sel<targetSize-errSize){
757 inf = size;
758 if(sup) size+=(sup-inf)/2;
759 else size*=2;
760 }
761 else if(sel>targetSize+errSize){
762 sup = size;
763 if(inf) size-=(sup-inf)/2;
764 else size/=2;
765 }
766 else done=true;
767 }while(!done);
768
769 //perform clustering. this set some vertesec of m as selected
770 ClusteringGrid.SelectPointSet(m);
771
772 ComputeAPSS(m); //compute curvature
773
774 //compute curvature histogram just on selected verteces
775 Histogram<ScalarType> hist = Histogram<ScalarType>();
776 tri::Stat<MeshType>::ComputePerVertexQualityHistogram(m, hist, true);
777 float vmin = hist.Percentile(param.featureDesc[i].lower_bound); float vmax = hist.Percentile(param.featureDesc[i].upper_bound);
778
779 //If curvature is beetween bounds and vertex is selected and it is not a boundary, curvature is stored in the feature, otherwise the feature is set to an empty value.
780 for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
781 {
782 if( ((*vi).IsS()) & ((!(*vi).IsB()) & ((*vi).Q()<=vmin) | ((*vi).Q()>=vmax) & (!FeatureType::isNullValue((*vi).Q()))) )
783 fh[vi].description[i] = (*vi).Q();
784 else
785 fh[vi].description[i] = FeatureType::getNullValue();
786
787 if(cb){ progBar+=offset; cb((int)progBar,"Computing features..."); }
788 }
789 }
790 return true;
791 }
792
793 template<class MESH_TYPE, int dim>
CreateSamplingMesh()794 MESH_TYPE* APSSCurvatureFeature<MESH_TYPE,dim>::CreateSamplingMesh()
795 {
796 MeshType* m = new MeshType();
797 PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(*m, true);
798 if(!tri::Allocator<MeshType>::IsValidHandle(*m,fh)){
799 if(m) delete m;
800 return NULL;
801 }
802 return m;
803 }
804
805 template<class MESH_TYPE, int dim>
SetupSamplingStructures(MeshType & m,typename MeshType::template PerVertexAttributeHandle<FeatureType> & fh,MeshType * samplingMesh,vector<FeatureType * > * vecFeatures)806 int APSSCurvatureFeature<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
807 {
808 int countFeatures = 0;
809 PVAttributeHandle pmfh;
810 if(samplingMesh){
811 pmfh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(*samplingMesh);
812 if(!tri::Allocator<MeshType>::IsValidHandle(*samplingMesh,pmfh)) return 0;
813 }
814
815 //fill the vector with all persistent features.
816 for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi){
817 //check persistence beetween scales: if feature is persistent, add a pointer in vecFeatures
818 if( FeatureType::CheckPersistency(fh[vi])){
819 countFeatures++; //increment counter of valid features
820 if(vecFeatures) vecFeatures->push_back(&(fh[vi]));
821 if(samplingMesh){
822 tri::Allocator<MeshType>::AddVertices(*samplingMesh, 1);
823 samplingMesh->vert.back().ImportLocal(*vi);
824 pmfh[samplingMesh->vert.back()] = fh[vi];
825 }
826 }
827 }
828
829 return countFeatures;
830 }
831
832 template<class MESH_TYPE, int dim>
Subset(int k,MeshType & m,vector<FeatureType * > & vecSubset,int sampType,CallBackPos * cb)833 bool APSSCurvatureFeature<MESH_TYPE,dim>::Subset(int k, MeshType &m, vector<FeatureType*> &vecSubset, int sampType, CallBackPos *cb)
834 {
835 //variables needed for progress bar callback
836 float progBar = 0.0f;
837 float offset = 100.0f/(m.VertexNumber() + k);
838
839 //if attribute doesn't exist, return; else we can get a handle to the attribute
840 PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m);
841 if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
842
843 //create a vector to hold valid features that later will be sampled
844 vector<FeatureType*>* vecFeatures = NULL;
845 MeshType* poissonMesh = NULL;
846 if(sampType==0) vecFeatures = new vector<FeatureType*>();
847 else poissonMesh = CreateSamplingMesh();
848
849 //fill the vector with all persistent features.
850 int countFeatures = SetupSamplingStructures(m, fh, poissonMesh, vecFeatures);
851 if(countFeatures<k) k = countFeatures; //we can't extract more of what we got!
852
853 //perform different kinds of sampling
854 FeatureType** sampler = NULL;
855 switch(sampType){
856 case 0:{ //uniform sampling: uses vecFeatures
857 sampler = FeatureAlignment<MeshType, FeatureType>::FeatureUniform(*vecFeatures, &k);
858 break;
859 }
860 case 1:{ //poisson disk sampling: uses poissonMesh
861 sampler = FeatureAlignment<MeshType, FeatureType>::FeaturePoisson(*poissonMesh, &k);
862 break;
863 }
864 default: assert(0);
865 }
866
867 //store features into the returned vector
868 for(int i=0; i<k; ++i){
869 vecSubset.push_back(sampler[i]);
870 if(cb){ progBar+=offset; cb(int(progBar),"Extracting features..."); }
871 }
872
873 if(vecFeatures) delete vecFeatures; //clear useless data
874 if(poissonMesh) delete poissonMesh;
875 if(sampler) delete[] sampler;
876
877 return true;
878
879 ////UNCOMMENT FOLLOW TO GET A RARE FEATURE SELECTION////
880 /*
881 int histSize = 10000; //number of bins of the histogram
882 vector<FeatureType*>* sorted = new vector<FeatureType*>(); //vector that holds features sorted by bin cont
883
884 //compute min val e max val between all features; min and max are needed to bound the histogram
885 pair<float,float> minmax = FindMinMax(*vecFeatures, 0);
886
887 //fill multimap with features sorted by bin count
888 SortByBinCount(*vecFeatures, minmax.first, minmax.second, histSize, *sorted);
889
890 //select the first k entries from mulptimap, and put them into vecSubset
891 typename vector<FeatureType*>::iterator it = sorted->begin();
892 ///UNCOMMENT THIS LOOP FOR A SORTED SELECTION
893 for(int i=0; i<k & it!=sorted->end(); i++, it++)
894 {
895 (*it)->selected = true; //mark features as selected
896 vecSubset.push_back(*it);
897 }
898 ///UNCOMMENT THIS LOOP FOR A EQUALIZED SELECTION
899 int off = int(sorted->size()/k);
900 for(it = sorted->begin(); it<sorted->end(); it=it+off)
901 {
902 (*it)->selected = true; //mark features as selected
903 vecSubset.push_back(*it);
904 }
905
906 //delete vector and set pointer to NULL for safety
907 if(vecFeatures) delete vecFeatures; //clear useless data
908 sorted->clear();
909 delete sorted;
910 sorted = NULL;
911 return true;
912 */
913 }
914
915 //scan the vector of features and return a pair containig the min and max description values
FindMinMax(vector<FeatureType * > & vec,int scale)916 template<class MESH_TYPE, int dim> pair<float,float> APSSCurvatureFeature<MESH_TYPE,dim>::FindMinMax(vector<FeatureType*>& vec, int scale)
917 {
918 assert(scale>=0 && scale<FeatureType::GetFeatureDimension());
919
920 typename vector<FeatureType*>::iterator vi;
921 pair<float,float> minmax = make_pair(numeric_limits<float>::max(),-numeric_limits<float>::max());
922 for(vi = vec.begin(); vi!=vec.end(); ++vi)
923 {
924 if( !FeatureType::isNullValue((*vi)->description[scale])) //invalid values are discarded
925 {
926 if( (*vi)->description[scale] < minmax.first) minmax.first=(*vi)->description[scale];
927 if( (*vi)->description[scale] > minmax.second ) minmax.second=(*vi)->description[scale];
928 }
929 }
930 return minmax;
931 }
932
933 //fill a vector that holds features pointers sorted by bin count; i.e first all the very infrequent features and last all the very frequent ones.
SortByBinCount(vector<FeatureType * > & vecFeatures,float min,float max,int histSize,vector<FeatureType * > & sorted)934 template<class MESH_TYPE, int dim> void APSSCurvatureFeature<MESH_TYPE,dim>::SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted)
935 {
936 //vector to hold bins ranges
937 vector<float>* bins = new vector<float>(histSize, 0);
938 //vector to count content of each bin
939 vector<int> *hToC = new vector<int>(histSize, 0);
940 //multimap to keep track of features for each bin
941 multimap<int, FeatureType* >* hToF = new multimap<int, FeatureType* >();
942 //multimap to store pairs of (count, FeatureType*). multimap is naturally sorted by count.
943 multimap<int, FeatureType* >* cToF = new multimap<int, FeatureType* >();
944
945 //offset beetween min and max is divided into histSize uniform bins
946 for(unsigned int i = 0; i<bins->size(); i++) bins->at(i) = min + i*(max-min)/float(histSize);
947
948 //build histogram, hToF and hToC; all with just one features scan
949 typename multimap<int, FeatureType* >::iterator hfIt = hToF->begin();
950 typename vector<FeatureType*>::iterator vi;
951 for(vi = vecFeatures.begin(); vi!=vecFeatures.end(); ++vi)
952 {
953 float val = (*vi)->description[0];
954 // lower_bound returns an iterator pointing to the first element "not less than" val, or end() if every element is less than val.
955 typename vector<float>::iterator it = lower_bound(bins->begin(),bins->end(),val);
956 //if val is out of range, skip iteration and take in account next val
957 if(it==bins->begin() || it==bins->end()) continue;
958 //determine in which bin got to insert val
959 int binId = (it - bins->begin())-1;
960 assert(binId>=0);
961 assert (bins->at(binId) < val);
962 assert (val <= bins->at(binId+1));
963
964 //increment bin count
965 hToC->at(binId)++;
966 //remember in which bin has been inserted this feature
967 hfIt = hToF->insert(hfIt,make_pair(binId, (*vi)));
968 }
969
970 //delete bins and set pointer to NULL for safety
971 bins->clear();
972 delete bins;
973 bins = NULL;
974
975 //for all bin index insert in the multimap an entry with key bin count and value a feature. Entries are naturally
976 //sorted by key in the multimap
977 typename multimap<int, FeatureType* >::iterator cfIt = cToF->begin();
978 pair< typename multimap<int, FeatureType* >::iterator, typename multimap<int, FeatureType* >::iterator> range;
979 for(unsigned int i=0; i<hToC->size(); i++)
980 {
981 //if bin count is zero, then skip; nothing to put in the multimap
982 if(hToC->at(i)!=0)
983 {
984 range = hToF->equal_range(i);
985 assert(range.first!=range.second);
986 for (; range.first!=range.second; ++range.first)
987 {
988 cfIt = cToF->insert( cfIt, make_pair(hToC->at(i), (*range.first).second) );
989 }
990 }
991 }
992
993 typename multimap<int, FeatureType* >::iterator it;
994 for(it = cToF->begin(); it != cToF->end(); it++)
995 sorted.push_back((*it).second);
996
997 assert(sorted.size()==cToF->size());
998
999 //delete all ausiliary structures and set pointers to NULL for safety
1000 hToF->clear();
1001 delete hToF;
1002 hToF = NULL;
1003 hToC->clear();
1004 delete hToC;
1005 hToC = NULL;
1006 cToF->clear();
1007 delete cToF;
1008 cToF = NULL;
1009 }
1010
SpatialSelection(vector<FeatureType * > & container,vector<FeatureType * > & vec,int k,float radius)1011 template<class MESH_TYPE, int dim> int APSSCurvatureFeature<MESH_TYPE,dim>::SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius)
1012 {
1013 //variables to manage the kDTree which works on features position
1014 ANNpointArray dataPts = NULL; // data points
1015 ANNpoint queryPnt = NULL; // query points
1016 ANNkd_tree* kdTree = NULL; // search structure
1017
1018 queryPnt = annAllocPt(3);
1019
1020 typename vector<FeatureType*>::iterator ci = container.begin();
1021 while(ci != container.end() && vec.size()<k)
1022 {
1023 if(vec.size()==0)
1024 {
1025 vec.push_back(*ci);
1026 (*ci)->selected = true;
1027 ci++;
1028 continue;
1029 }
1030
1031 if(dataPts){ annDeallocPts(dataPts); dataPts = NULL; }
1032 if(kdTree){ delete kdTree; kdTree = NULL; }
1033 dataPts = annAllocPts(vec.size(), 3);
1034
1035 for(int i = 0; i < vec.size(); i++)
1036 {
1037 for (int j = 0; j < 3; j++)
1038 {
1039 (dataPts[i])[j] = (ANNcoord)(vec.at(i)->pos[j]);
1040 }
1041 }
1042 //build search structure
1043 kdTree = new ANNkd_tree(dataPts,vec.size(),3);
1044
1045 for (int j = 0; j < 3; j++)
1046 {
1047 queryPnt[j] = (ANNcoord)((*ci)->pos[j]);
1048 }
1049
1050 //if there aren't features yet selected in the range distance
1051 if(!kdTree->annkFRSearch(queryPnt, math::Sqr(radius), 0, NULL, NULL, 0.0))
1052 {
1053 vec.push_back(*ci);
1054 (*ci)->selected = true;
1055 }
1056 ci++;
1057 }
1058
1059 if(dataPts){ annDeallocPts(dataPts); dataPts = NULL; }
1060 if(queryPnt){ annDeallocPt(queryPnt); queryPnt = NULL; }
1061 if(kdTree){ delete kdTree; kdTree = NULL; }
1062
1063 return vec.size();
1064 }
1065