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