1 /****************************************************************************
2 * VCGLib                                                            o o     *
3 * Visual and Computer Graphics Library                            o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2004-2016                                           \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 #ifndef _AUTOALIGN_4PCS_H_
24 #define _AUTOALIGN_4PCS_H_
25 
26 /**
27 implementation of the 4PCS method from the paper:
28 "4-Points Congruent Sets for Robust Pairwise Surface Registration"
29 D.Aiger, N.Mitra D.Cohen-Or, SIGGRAPH 2008
30 ps: the name of the variables are out of vcg standard but like the one
31 used in the paper pseudocode.
32 */
33 
34 #include <vcg/complex/complex.h>
35 #include <vcg/space/point_matching.h>
36 #include <vcg/complex/algorithms/closest.h>
37 #include <vcg/complex/algorithms/point_sampling.h>
38 #include <vcg/math/random_generator.h>
39 #include <ctime>
40 namespace vcg{
41 namespace tri{
42 
43 template <class MeshType>
44 class FourPCS {
45 public:
46     /* mesh only for using spatial indexing functions (to remove) */
47   class PVertex;    // dummy prototype never used
48   class PFace;
49 
50   class PUsedTypes: public vcg::UsedTypes < vcg::Use<PVertex>::template AsVertexType,
51                                             vcg::Use<PFace  >::template AsFaceType >{};
52 
53   class PVertex : public vcg::Vertex< PUsedTypes,vcg::vertex::BitFlags,vcg::vertex::Coord3f,vcg::vertex::Mark>{};
54   class PFace   : public vcg::Face<   PUsedTypes> {};
55   class PMesh   : public vcg::tri::TriMesh< std::vector<PVertex>, std::vector<PFace> > {};
56 
57   typedef typename MeshType::ScalarType ScalarType;
58   typedef typename MeshType::CoordType CoordType;
59   typedef typename vcg::Matrix44<ScalarType> Matrix44x;
60   typedef typename vcg::Box3<ScalarType> Box3x;
61 
62   typedef typename MeshType::VertexIterator VertexIterator;
63   typedef typename MeshType::VertexPointer VertexPointer;
64   typedef typename MeshType::VertexType VertexType;
65   typedef vcg::Point4< vcg::Point3<ScalarType> > FourPoints;
66   typedef vcg::GridStaticPtr<typename PMesh::VertexType, ScalarType > GridType;
67 
68   /* class for Parameters */
69   struct Param
70   {
71     ScalarType overlap;    // overlap estimation as a percentage of overlapping points.
72 
73     int sampleNumP;        // number of samples on moving mesh P (it determines the sampling radius to be used to sample Q too)
74     float samplingRadius;
75 
76     ScalarType deltaPerc;  // Approximation Level (expressed as a percentage of the avg distance between samples)
77     ScalarType deltaAbs;   // Approximation Level
78     int feetSize;          // how many points in the neighborhood of each of the 4 points
79     int scoreFeet;         // how many of the feetsize points must match (max feetsize*4) to try an early interrupt
80     ScalarType cosAngle;   // max admittable angle that can be admitted between matching points in alignments (expressed as cos(ang) )
81     int seed;              // random seed used. Need for repeatability.
82 
DefaultParam83     void Default(){
84       overlap = 0.5;
85       sampleNumP=500;
86       samplingRadius=0;
87 
88       deltaPerc = 0.5;
89       deltaAbs = 0;
90       feetSize = 25;
91       scoreFeet = 50;
92       seed =0;
93       cosAngle = 0; // normals must differ more than 90 degree to be considered bad.
94     }
95   };
96 
97   struct Stat
98   {
StatStat99     Stat() : initTime(0),selectCoplanarBaseTime(0),findCongruentTime(0),testAlignmentTime(0)
100     {}
101     clock_t initTime;
102     clock_t selectCoplanarBaseTime;
103     clock_t findCongruentTime;
104     clock_t testAlignmentTime;
initStat105     float init()   {return 1000.0f*float(initTime)/float(CLOCKS_PER_SEC);}
selectStat106     float select() {return 1000.0f*float(selectCoplanarBaseTime)/float(CLOCKS_PER_SEC);}
findCongruentStat107     float findCongruent() {return 1000.0f*float(findCongruentTime)/float(CLOCKS_PER_SEC);}
testAlignmentStat108     float testAlignment() {return 1000.0f*float(testAlignmentTime)/float(CLOCKS_PER_SEC);}
109   };
110 
111   class Couple
112   {
113   public:
114     VertexPointer p0,p1;
Couple(VertexPointer i,VertexPointer j,float d)115     Couple(VertexPointer i, VertexPointer j, float d) : p0(i),p1(j),dist(d){}
116     float dist;
117     bool operator < (const   Couple & o) const {return dist < o.dist;}
118     VertexPointer operator[](const int &i) const {return (i==0)? this->p0 : this->p1;}
119   };
120 
121   struct Candidate
122   {
CandidateCandidate123     Candidate():score(0){}
CandidateCandidate124     Candidate(FourPoints _p, vcg::Matrix44<ScalarType>_T):p(_p),T(_T){}
125     FourPoints  p;
126     vcg::Matrix44<ScalarType> T;
127     int score;
128     inline bool operator <(const Candidate & o) const {return score > o.score;}
129   };
130 
131 
132   // class for the point  'ei'
133   struct EPoint{
EPointEPoint134     EPoint(vcg::Point3<ScalarType> _p, int _i):pos(_p),pi(_i){}
135     vcg::Point3<ScalarType> pos;
136     int pi;        //index to R[1|2]
GetBBoxEPoint137     void GetBBox(vcg::Box3<ScalarType> & b){b.Add(pos);}
138   };
139 
140 
141   Param par;    /// parameters
142   Stat stat;
143 
144   MeshType  *P;    // Moving Mesh (from which the coplanar base is selected)
145   MeshType  *Q;    // Fixed Mesh  (mesh where to find the correspondences)
146 
147   math::MarsenneTwisterRNG rnd;
148 
149   std::vector<VertexPointer> subsetQ;  // subset of the vertices in Q
150   std::vector<VertexPointer> subsetP; // random selection on P
151 
152   ScalarType side;               // side
153 
154   PMesh     Invr;                // invariants
155   std::vector< Candidate > U;    // the
156   int iwinner;                   // winner == U[iwinner]
157   std::vector<FourPoints> bases; // used bases
158   std::vector<VertexType*> ExtB[4]; // selection of vertices "close" to the four point
159 
160   vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridQ;
161   vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridP;
162 
163   /* returns the closest point between to segments x1-x2 and x3-x4.  */
IntersectionLineLine(const CoordType & x1,const CoordType & x2,const CoordType & x3,const CoordType & x4,CoordType & x)164     void IntersectionLineLine(const CoordType & x1,const CoordType & x2,const CoordType & x3,const CoordType & x4, CoordType&x)
165     {
166       CoordType a = x2-x1, b = x4-x3, c = x3-x1;
167       x = x1 + a * ((c^b).dot(a^b)) / (a^b).SquaredNorm();
168     }
169 
170 
Init(MeshType & _movP,MeshType & _fixQ)171 void Init(MeshType &_movP,MeshType &_fixQ)
172 {
173   clock_t t0= clock();
174   P = &_movP;
175   Q = &_fixQ;
176   tri::UpdateBounding<MeshType>::Box(*P);
177   if(par.seed==0) rnd.initialize(time(0));
178   else rnd.initialize(par.seed);
179 
180   ugridQ.Set(Q->vert.begin(),Q->vert.end());
181   ugridP.Set(P->vert.begin(),P->vert.end());
182 
183   if(par.samplingRadius==0)
184     par.samplingRadius = tri::ComputePoissonDiskRadius(*P,par.sampleNumP);
185   tri::PoissonPruning(*P, subsetP, par.samplingRadius, par.seed);
186   tri::PoissonPruning(*Q, subsetQ, par.samplingRadius, par.seed);
187   par.deltaAbs = par.samplingRadius * par.deltaPerc;
188   side = P->bbox.Dim()[P->bbox.MaxDim()]*par.overlap; //rough implementation
189   stat.initTime+=clock()-t0;
190 }
191 
192 
193 // Try to select four coplanar points such that they are at least side distance and
194 //
SelectCoplanarBase(FourPoints & B,ScalarType & r1,ScalarType & r2)195 bool SelectCoplanarBase(FourPoints &B, ScalarType &r1, ScalarType &r2)
196 {
197   clock_t t0= clock();
198 
199   // choose the inter point distance
200   ScalarType dtol = side*0.1; //rough implementation
201 
202 
203   // **** first point: random
204   B[0] = P->vert[ rnd.generate(P->vert.size())].P();
205 
206   // **** second point: a random point at distance side +-dtol
207   size_t i;
208   for(i = 0; i < P->vert.size(); ++i){
209     int id = rnd.generate(P->vert.size());
210     ScalarType dd = (P->vert[id].P() - B[0]).Norm();
211     if(  ( dd < side + dtol) && (dd > side - dtol)){
212       B[1] = P->vert[id].P();
213       break;
214     }
215   }
216   if(i ==  P->vert.size()) return false;
217 
218   // **** third point: at distance less than side*0.8 from middle way between B[0] and B[1]
219   const CoordType middle = (B[0]+B[1])/2.0;
220   for(i = 0; i < P->vert.size(); ++i){
221     int id = rnd.generate(P->vert.size());
222     if( Distance(P->vert[id].P(),middle) < side*0.8 ){
223       B[2] = P->vert[id].P();
224       break;
225     }
226   }
227   if(i ==  P->vert.size()) return false;
228 
229   // **** fourth point:
230   ScalarType cpr = rnd.generate01();
231   CoordType crossP = B[0] *(1-cpr)+B[1]*cpr;
232   CoordType B4 = B[2]+(crossP-B[2]).Normalize()*side;
233   CoordType n = ((B[0]-B[1]).normalized() ^ (B[2]-B[1]).normalized()).normalized();
234   ScalarType radius = dtol;
235 
236   std::vector<typename MeshType::VertexType*> closests;
237   std::vector<ScalarType> distances;
238   std::vector<CoordType> points;
239 
240   vcg::tri::GetInSphereVertex<
241       MeshType,
242       vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
243       std::vector<typename MeshType::VertexType*>,
244       std::vector<ScalarType>,
245       std::vector<CoordType>
246       >(*P,ugridP,B4,radius,closests,distances,points);
247 
248   if(closests.empty())
249     return false;
250   int bestInd = -1;   ScalarType bestv=std::numeric_limits<float>::max();
251   for(i = 0; i <closests.size(); ++i){
252     ScalarType dist_from_plane = fabs((closests[i]->P() - B[1]).normalized().dot(n));
253     if( dist_from_plane < bestv){
254       bestv = dist_from_plane;
255       bestInd = i;
256     }
257   }
258   if(bestv >dtol)
259     return false;
260   B[3] =  closests[bestInd]->P();
261 
262   //printf("B[3] %d\n", (typename MeshType::VertexType*)closests[best] - &(*P->vert.begin()));
263 
264   // compute r1 and r2
265   CoordType x;
266   //        std::swap(B[1],B[2]);
267   IntersectionLineLine(B[0],B[1],B[2],B[3],x);
268 
269   r1 = (x - B[0]).dot(B[1]-B[0]) / (B[1]-B[0]).SquaredNorm();
270   r2 = (x - B[2]).dot(B[3]-B[2]) / (B[3]-B[2]).SquaredNorm();
271 
272   if( ((B[0]+(B[1]-B[0])*r1)-(B[2]+(B[3]-B[2])*r2)).Norm() > par.deltaAbs )
273     return false;
274 
275   radius  = side*0.5;
276   std::vector< CoordType > samples;
277   std::vector<ScalarType > dists;
278 
279   for(int i  = 0 ; i< 4; ++i){
280     vcg::tri::GetKClosestVertex<
281         MeshType,
282         vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
283         std::vector<VertexType*>,
284         std::vector<ScalarType>,
285         std::vector< CoordType > >(*P,ugridP, par.feetSize ,B[i],radius, ExtB[i], dists, samples);
286   }
287 
288   //qDebug("ExtB %i",ExtB[0].size()+ExtB[1].size()+ExtB[2].size()+ExtB[3].size());
289   stat.selectCoplanarBaseTime+=clock()-t0;
290   return true;
291 }
292 
IsTransfCongruent(const FourPoints & B,const FourPoints & fp,vcg::Matrix44<ScalarType> & mat)293 bool IsTransfCongruent(const FourPoints &B, const FourPoints &fp, vcg::Matrix44<ScalarType> & mat)
294 {
295   std::vector<vcg::Point3<ScalarType> > fix(4);
296   std::vector<vcg::Point3<ScalarType> > mov(4);
297   for(int i = 0 ; i < 4; ++i) {
298     mov[i]=B[i];
299     fix[i]=fp[i];
300   }
301 
302   if(fabs( Distance(fix[0],fix[1]) - Distance(mov[0],mov[1]) ) > par.deltaAbs) return false;
303   if(fabs( Distance(fix[0],fix[2]) - Distance(mov[0],mov[2]) ) > par.deltaAbs) return false;
304   if(fabs( Distance(fix[0],fix[3]) - Distance(mov[0],mov[3]) ) > par.deltaAbs) return false;
305   if(fabs( Distance(fix[1],fix[2]) - Distance(mov[1],mov[2]) ) > par.deltaAbs) return false;
306   if(fabs( Distance(fix[1],fix[3]) - Distance(mov[1],mov[3]) ) > par.deltaAbs) return false;
307   if(fabs( Distance(fix[2],fix[3]) - Distance(mov[2],mov[3]) ) > par.deltaAbs) return false;
308 
309   vcg::ComputeRigidMatchMatrix(fix,mov,mat);
310 
311   ScalarType maxSquaredDistance = 0.0;
312   for(int i = 0; i < 4; ++i)
313     maxSquaredDistance =std::max(maxSquaredDistance, SquaredDistance(mat * mov[i] ,fix[i]));
314   return  sqrt(maxSquaredDistance)  < par.deltaAbs;
315 }
316 
317 /// Compute the vector R1 of couple of points on FixQ at a given distance.
318 /// Used by FindCongruent
ComputeR1(std::vector<Couple> & R1)319 void ComputeR1(std::vector<Couple > &R1)
320 {
321   R1.clear();
322   for(size_t vi = 0; vi  < subsetQ.size(); ++vi)
323     for(size_t vj = vi; vj < subsetQ.size(); ++vj){
324       ScalarType d = Distance(subsetQ[vi]->P(),subsetQ[vj]->P());
325       if( (d < side+par.deltaAbs))
326       {
327         R1.push_back(Couple(subsetQ[vi],subsetQ[vj], d));
328         R1.push_back(Couple(subsetQ[vj],subsetQ[vi], d));
329       }
330     }
331 
332   std::sort(R1.begin(),R1.end());
333 }
334 
335 // Find congruent elements of a base B, on Q, with approximation delta
336 // and put them in the U vector.
FindCongruent(const std::vector<Couple> & R1,const FourPoints & B,const ScalarType r1,const ScalarType r2)337 bool FindCongruent(const std::vector<Couple > &R1, const FourPoints &B, const ScalarType r1, const ScalarType r2)
338 {
339   clock_t t0=clock();
340   int n_base=0;
341   bool done = false;
342   int n_closests = 0, n_congr = 0;
343   int ac =0 ,acf = 0,tr = 0,trf =0;
344   ScalarType d1,d2;
345   d1 = (B[1]-B[0]).Norm();
346   d2 = (B[3]-B[2]).Norm();
347 
348   typename std::vector<Couple>::const_iterator bR1,eR1,bR2,eR2,ite;
349   bR1 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d1-par.deltaAbs));
350   eR1 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d1+par.deltaAbs));
351   bR2 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d2-par.deltaAbs));
352   eR2 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d2+par.deltaAbs));
353 
354   // in  [bR1,eR1) there are all the pairs at a distance d1 +- par.delta
355   // in  [bR1,eR1) there are all the pairs at a distance d2 +- par.delta
356 
357   if(bR1 == R1.end()) return false;// if there are no such pairs return
358   if(bR2 == R1.end()) return false; // if there are no such pairs return
359 
360   // put [bR1,eR1) in a mesh to have the search operator for free (lazy me)
361   Invr.Clear();
362   typename PMesh::VertexIterator vii;
363   int i = &(*bR1)-&(*R1.begin());
364   for(ite = bR1; ite != eR1;++ite){
365     vii = vcg::tri::Allocator<PMesh>::AddVertices(Invr,1);
366     //      (*vii).P() = Q->vert[R1[i][0]].P() + (Q->vert[R1[i][1]].P()-Q->vert[R1[i][0]].P()) * r1;
367     (*vii).P() .Import(         ite->p0->P() + (        ite->p1->P() -       ite->p0->P()) * r1);
368     ++i;
369   }
370   if(Invr.vert.empty() ) return false;
371 
372   // per vertex attribute 'index' remaps a vertex of Invr to its corresponding point in R1
373   typename PMesh::template PerVertexAttributeHandle<int> id = vcg::tri::Allocator<PMesh>::template AddPerVertexAttribute<int>(Invr,std::string("index"));
374   i = &(*bR1)-&(*R1.begin());
375   for(vii = Invr.vert.begin(); vii != Invr.vert.end();++vii,++i)  id[vii] = i;
376 
377   vcg::tri::UpdateBounding<PMesh>::Box(Invr);
378 
379 
380   std::vector<EPoint> R2inv;
381   i = &(*bR2)-&(*R1.begin());
382   // R2inv contains all the points generated by the couples in R2 (with the reference to remap into R2)
383   for(ite = bR2; ite != eR2;++ite){
384     //        R2inv.push_back( EPoint( Q->vert[R1[i][0]].P() + (Q->vert[R1[i][1]].P()-Q->vert[R1[i][0]].P()) * r2,i));
385     R2inv.push_back( EPoint( R1[i].p0->P() + (R1[i].p1->P() - R1[i].p0->P()) * r2,i));
386     ++i;
387   }
388 
389   GridType ugrid; // griglia
390   ugrid.Set(Invr.vert.begin(),Invr.vert.end());
391   n_closests = 0; n_congr = 0; ac =0 ; acf = 0; tr = 0; trf = 0;
392   printf("R2Inv.size  = %d \n",R2inv.size());
393   for(unsigned int i = 0 ; i < R2inv.size() ; ++i)
394   {
395     std::vector<typename PMesh::VertexType*> closests;
396 
397     // for each point in R2inv get all the points in R1 closer than par.delta
398     vcg::Matrix44<ScalarType> mat;
399     Box3x bb;
400     bb.Add(R2inv[i].pos+CoordType(par.deltaAbs,par.deltaAbs, par.deltaAbs));
401     bb.Add(R2inv[i].pos-CoordType(par.deltaAbs,par.deltaAbs, par.deltaAbs));
402 
403     vcg::tri::GetInBoxVertex<PMesh,GridType,std::vector<typename PMesh::VertexType*> >
404         (Invr,ugrid,bb,closests);
405 
406     if(closests.size() > 5)
407       closests.resize(5);
408 
409     n_closests+=closests.size();
410     for(unsigned int ip = 0; ip < closests.size(); ++ip)
411     {
412       FourPoints p;
413       p[0] = R1[id[closests[ip]]][0]->cP();
414       p[1] = R1[id[closests[ip]]][1]->cP();
415       p[2] = R1[ R2inv[i].pi][0]->cP();
416       p[3] = R1[ R2inv[i].pi][1]->cP();
417 
418       n_base++;
419       if(!IsTransfCongruent(B,p,mat)) {
420         trf++;
421       }
422       else{
423         tr++;
424         n_congr++;
425         Candidate c(p,mat);
426         EvaluateAlignment(c);
427 
428         if( c.score > par.scoreFeet)
429           U.push_back(c);
430       }
431     }
432   }
433 
434   vcg::tri::Allocator<PMesh>::DeletePerVertexAttribute(Invr,id);
435   printf("n_closests %5d = (An %5d ) + ( Tr %5d ) + (OK) %5d\n",n_closests,acf,trf,n_congr);
436 
437   stat.findCongruentTime += clock()-t0;
438   return done;
439 }
440 
441 
EvaluateSample(Candidate & fp,const CoordType & tp,const CoordType & np)442 int EvaluateSample(Candidate & fp, const CoordType & tp, const CoordType & np)
443 {
444   CoordType ttp = fp.T * tp;
445   vcg::Point4<ScalarType> np4 = fp.T * vcg::Point4<ScalarType>(np[0],np[1],np[2],0.0);
446   CoordType tnp(np4[0],np4[1],np4[2]);
447 
448   ScalarType   dist ;
449   VertexType* v = vcg::tri::GetClosestVertex(*Q, ugridQ, ttp, par.deltaAbs*2.0,  dist  );
450 
451   if(v!=0)
452   {
453     if( v->N().dot(tnp) > par.cosAngle )  return 1;
454     else return -1;
455   }
456   else return 0;
457 }
458 
459 // Check a candidate against the small subset of points ExtB
EvaluateAlignment(Candidate & fp)460 void EvaluateAlignment(Candidate  & fp){
461         int n_delta_close = 0;
462         for(int i  = 0 ; i< 4; ++i) {
463             for(unsigned int j = 0; j < ExtB[i].size();++j){
464                 n_delta_close+=EvaluateSample(fp, ExtB[i][j]->P(), ExtB[i][j]->cN());
465             }
466         }
467         fp.score = n_delta_close;
468 }
469 
TestAlignment(Candidate & fp)470 void TestAlignment(Candidate  & fp)
471 {
472   clock_t t0 = clock();
473   int n_delta_close = 0;
474   for(unsigned int j = 0; j < subsetP.size();++j){
475     CoordType np = subsetP[j]->N();
476     CoordType tp = subsetP[j]->P();
477     n_delta_close+=EvaluateSample(fp,tp,np);
478   }
479   fp.score =  n_delta_close;
480   stat.testAlignmentTime += clock()-t0;
481 }
482 
483 
Align(Matrix44x & result,vcg::CallBackPos * cb)484 bool Align(Matrix44x & result, vcg::CallBackPos * cb )
485 {
486   int maxAttempt =100;
487   int scoreThr = par.sampleNumP*0.8;
488 
489   Candidate bestC;
490 
491   std::vector<Couple > R1;
492   ComputeR1(R1);
493   for(int i  = 0; i  < maxAttempt && bestC.score<scoreThr ; ++i )
494   {
495     FourPoints B;
496     ScalarType r1,r2;
497     if(SelectCoplanarBase(B,r1,r2))
498     {
499       U.clear();
500       FindCongruent(R1,B,r1,r2);
501       qDebug("Attempt %i found %i candidate best score %i",i,U.size(),bestC.score);
502       for(size_t i = 0 ; i <  U.size() ;++i)
503       {
504         TestAlignment(U[i]);
505         if(U[i].score > bestC.score)
506           bestC = U[i];
507       }
508     }
509   }
510   result = bestC.T;
511   return bestC.score >0;
512 }
513 
Align(int L,Matrix44x & result,vcg::CallBackPos * cb)514 bool Align(int L, Matrix44x & result, vcg::CallBackPos * cb )
515 {
516     int bestv = 0;
517     bool found;
518     int n_tries = 0;
519     U.clear();
520 
521     if(L==0)
522     {
523         // overlap is expressed as the probability that a point in P(mov) can be found in Q (fix)
524         L = (log(1.0-0.9) / log(1.0-pow((float)par.overlap,3.f)))+1;
525         printf("using %d bases\n",L);
526     }
527     std::vector<Couple > R1;
528     ComputeR1(R1);
529 
530     for(int t  = 0; t  < L; ++t )
531     {
532       FourPoints B;
533       ScalarType r1,r2;
534       do
535       {
536         n_tries = 0;
537         do
538         {
539           n_tries++;
540           found = SelectCoplanarBase(B,r1,r2);
541         }
542         while(!found && (n_tries < 50));
543         if(!found) {
544           par.overlap*=0.9;
545           side = P->bbox.Dim()[P->bbox.MaxDim()]*par.overlap; //rough implementation
546           ComputeR1(R1);
547         }
548       } while (!found && (par.overlap >0.1));
549 
550       if(par.overlap < 0.1) {
551         printf("FAILED");
552         return false;
553       }
554       bases.push_back(B);
555       if(cb) cb(t*100/L,"Trying bases");
556       if(FindCongruent(R1,B,r1,r2))
557         break;
558     }
559 
560     if(U.empty()) return false;
561 
562 //    std::sort(U.begin(),U.end());
563     if(cb) cb(90,"TestAlignment");
564     bestv  = -std::numeric_limits<float>::max();
565     iwinner = 0;
566 
567     for(int i = 0 ; i <  U.size() ;++i)
568      {
569         TestAlignment(U[i]);
570         if(U[i].score > bestv){
571             bestv = U[i].score;
572             iwinner = i;
573             }
574     }
575 
576     result = U[iwinner].T;
577     Invr.Clear();
578     return true;
579 }
580 
581 }; // end class
582 
583 } // namespace tri
584 } // namespace vcg
585 #endif
586