00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifndef __VCG_TRIMESHCOLLAPSE_QUADRIC__
00025 #define __VCG_TRIMESHCOLLAPSE_QUADRIC__
00026
00027 #include<vcg/math/quadric.h>
00028 #include<vcg/simplex/face/pos.h>
00029 #include<vcg/complex/algorithms/update/flag.h>
00030 #include<vcg/complex/algorithms/update/topology.h>
00031 #include<vcg/complex/algorithms/update/bounding.h>
00032 #include<vcg/complex/algorithms/local_optimization/tri_edge_collapse.h>
00033 #include<vcg/complex/algorithms/local_optimization.h>
00034 #include<vcg/complex/algorithms/stat.h>
00035
00036
00037 namespace vcg{
00038 namespace tri{
00039
00040
00041
00042
00072
00073 template <class VERTEX_TYPE>
00074 class QInfoStandard
00075 {
00076 public:
00077 QInfoStandard(){}
00078 static void Init(){}
00079 static math::Quadric<double> &Qd(VERTEX_TYPE &v) {return v.Qd();}
00080 static math::Quadric<double> &Qd(VERTEX_TYPE *v) {return v->Qd();}
00081 static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE *) {return 1.0;}
00082 static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE &) {return 1.0;}
00083 static void Merge(VERTEX_TYPE & , VERTEX_TYPE const & ){}
00084 };
00085
00086
00087 class TriEdgeCollapseQuadricParameter : public BaseParameterClass
00088 {
00089 public:
00090 double BoundaryWeight;
00091 double CosineThr;
00092 bool FastPreserveBoundary;
00093 bool NormalCheck;
00094 double NormalThrRad;
00095 bool OptimalPlacement;
00096 bool PreserveTopology;
00097 bool PreserveBoundary;
00098 double QuadricEpsilon;
00099 bool QualityCheck;
00100 bool QualityQuadric;
00101 double QualityThr;
00102 bool QualityWeight;
00103 double QualityWeightFactor;
00104 double ScaleFactor;
00105 bool ScaleIndependent;
00106 bool UseArea;
00107 bool UseVertexWeight;
00108
00109 void SetDefaultParams()
00110 {
00111 BoundaryWeight=.5;
00112 CosineThr=cos(M_PI/2);
00113 FastPreserveBoundary=false;
00114 NormalCheck=false;
00115 NormalThrRad=M_PI/2;
00116 OptimalPlacement=true;
00117 PreserveBoundary = false;
00118 PreserveTopology = false;
00119 QuadricEpsilon =1e-15;
00120 QualityCheck=true;
00121 QualityQuadric=false;
00122 QualityThr=.3;
00123 QualityWeight=false;
00124 QualityWeightFactor=100.0;
00125 ScaleFactor=1.0;
00126 ScaleIndependent=true;
00127 UseArea=true;
00128 UseVertexWeight=false;
00129 }
00130
00131 TriEdgeCollapseQuadricParameter() {this->SetDefaultParams();}
00132 };
00133
00134
00135 template<class TriMeshType, class VertexPair, class MYTYPE, class HelperType = QInfoStandard<typename TriMeshType::VertexType> >
00136 class TriEdgeCollapseQuadric: public TriEdgeCollapse< TriMeshType, VertexPair, MYTYPE>
00137 {
00138 public:
00139 typedef typename vcg::tri::TriEdgeCollapse< TriMeshType, VertexPair, MYTYPE > TEC;
00140 typedef typename TriEdgeCollapse<TriMeshType, VertexPair, MYTYPE>::HeapType HeapType;
00141 typedef typename TriEdgeCollapse<TriMeshType, VertexPair, MYTYPE>::HeapElem HeapElem;
00142 typedef typename TriMeshType::CoordType CoordType;
00143 typedef typename TriMeshType::ScalarType ScalarType;
00144 typedef typename TriMeshType::FaceType FaceType;
00145 typedef typename TriMeshType::VertexType VertexType;
00146 typedef typename TriMeshType::VertexIterator VertexIterator;
00147 typedef typename TriMeshType::FaceIterator FaceIterator;
00148 typedef typename vcg::face::VFIterator<FaceType> VFIterator;
00149 typedef math::Quadric< double > QuadricType;
00150 typedef TriEdgeCollapseQuadricParameter QParameter;
00151 typedef HelperType QH;
00152
00153
00154
00155 static std::vector<typename TriMeshType::VertexPointer> & WV(){
00156 static std::vector<typename TriMeshType::VertexPointer> _WV; return _WV;
00157 }
00158
00159 inline TriEdgeCollapseQuadric(){}
00160
00161 inline TriEdgeCollapseQuadric(const VertexPair &p, int i, BaseParameterClass *pp)
00162 {
00163 this->localMark = i;
00164 this->pos=p;
00165 this->_priority = ComputePriority(pp);
00166 }
00167
00168
00169 inline bool IsFeasible(BaseParameterClass *_pp){
00170 QParameter *pp=(QParameter *)_pp;
00171 if(!pp->PreserveTopology) return true;
00172
00173 bool res = ( EdgeCollapser<TriMeshType, VertexPair>::LinkConditions(this->pos) );
00174 if(!res) ++( TEC::FailStat::LinkConditionEdge() );
00175 return res;
00176 }
00177
00178 CoordType ComputePosition(BaseParameterClass *_pp)
00179 {
00180 QParameter *pp=(QParameter *)_pp;
00181 CoordType newPos = (this->pos.V(0)->P()+this->pos.V(1)->P())/2.0;
00182 if(pp->OptimalPlacement)
00183 {
00184 if((QH::Qd(this->pos.V(0)).Apply(newPos) + QH::Qd(this->pos.V(1)).Apply(newPos)) > 200.0*pp->QuadricEpsilon)
00185 newPos = ComputeMinimal();
00186 }
00187 else newPos=this->pos.V(1)->P();
00188 return newPos;
00189 }
00190
00191 void Execute(TriMeshType &m, BaseParameterClass *_pp)
00192 {
00193 QH::Qd(this->pos.V(1))+=QH::Qd(this->pos.V(0));
00194 EdgeCollapser<TriMeshType,VertexPair>::Do(m, this->pos, ComputePosition(_pp));
00195 }
00196
00197
00198
00199
00200 static void Finalize(TriMeshType &m, HeapType& , BaseParameterClass *_pp)
00201 {
00202 QParameter *pp=(QParameter *)_pp;
00203
00204
00205 if(pp->FastPreserveBoundary)
00206 {
00207 typename TriMeshType::VertexIterator vi;
00208 for(vi=m.vert.begin();vi!=m.vert.end();++vi)
00209 if(!(*vi).IsD()) (*vi).SetW();
00210 }
00211 if(pp->PreserveBoundary)
00212 {
00213 typename std::vector<typename TriMeshType::VertexPointer>::iterator wvi;
00214 for(wvi=WV().begin();wvi!=WV().end();++wvi)
00215 if(!(*wvi)->IsD()) (*wvi)->SetW();
00216 }
00217 }
00218
00219 static void Init(TriMeshType &m, HeapType &h_ret, BaseParameterClass *_pp)
00220 {
00221 QParameter *pp=(QParameter *)_pp;
00222
00223 typename TriMeshType::VertexIterator vi;
00224 typename TriMeshType::FaceIterator pf;
00225
00226 pp->CosineThr=cos(pp->NormalThrRad);
00227
00228 vcg::tri::UpdateTopology<TriMeshType>::VertexFace(m);
00229 vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m);
00230
00231 if(pp->FastPreserveBoundary)
00232 {
00233 for(pf=m.face.begin();pf!=m.face.end();++pf)
00234 if( !(*pf).IsD() && (*pf).IsW() )
00235 for(int j=0;j<3;++j)
00236 if((*pf).IsB(j))
00237 {
00238 (*pf).V(j)->ClearW();
00239 (*pf).V1(j)->ClearW();
00240 }
00241 }
00242
00243 if(pp->PreserveBoundary)
00244 {
00245 WV().clear();
00246 for(pf=m.face.begin();pf!=m.face.end();++pf)
00247 if( !(*pf).IsD() && (*pf).IsW() )
00248 for(int j=0;j<3;++j)
00249 if((*pf).IsB(j))
00250 {
00251 if((*pf).V(j)->IsW()) {(*pf).V(j)->ClearW(); WV().push_back((*pf).V(j));}
00252 if((*pf).V1(j)->IsW()) {(*pf).V1(j)->ClearW();WV().push_back((*pf).V1(j));}
00253 }
00254 }
00255
00256 InitQuadric(m,pp);
00257
00258
00259 if(IsSymmetric(pp))
00260 {
00261 for(vi=m.vert.begin();vi!=m.vert.end();++vi)
00262 if(!(*vi).IsD() && (*vi).IsRW())
00263 {
00264 vcg::face::VFIterator<FaceType> x;
00265 for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x){
00266 x.V1()->ClearV();
00267 x.V2()->ClearV();
00268 }
00269 for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++x )
00270 {
00271 assert(x.F()->V(x.I())==&(*vi));
00272 if((x.V0()<x.V1()) && x.V1()->IsRW() && !x.V1()->IsV()){
00273 x.V1()->SetV();
00274 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(x.V0(),x.V1()),TriEdgeCollapse< TriMeshType,VertexPair,MYTYPE>::GlobalMark(),_pp )));
00275 }
00276 if((x.V0()<x.V2()) && x.V2()->IsRW()&& !x.V2()->IsV()){
00277 x.V2()->SetV();
00278 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(x.V0(),x.V2()),TriEdgeCollapse< TriMeshType,VertexPair,MYTYPE>::GlobalMark(),_pp )));
00279 }
00280 }
00281 }
00282 }
00283 else
00284 {
00285 for(vi=m.vert.begin();vi!=m.vert.end();++vi)
00286 if(!(*vi).IsD() && (*vi).IsRW())
00287 {
00288 vcg::face::VFIterator<FaceType> x;
00289 UnMarkAll(m);
00290 for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x)
00291 {
00292 assert(x.F()->V(x.I())==&(*vi));
00293 if(x.V()->IsRW() && x.V1()->IsRW() && !IsMarked(m,x.F()->V1(x.I()))){
00294 h_ret.push_back( HeapElem( new MYTYPE( VertexPair (x.V(),x.V1()),TriEdgeCollapse< TriMeshType,VertexPair,MYTYPE>::GlobalMark(),_pp)));
00295 }
00296 if(x.V()->IsRW() && x.V2()->IsRW() && !IsMarked(m,x.F()->V2(x.I()))){
00297 h_ret.push_back( HeapElem( new MYTYPE( VertexPair (x.V(),x.V2()),TriEdgeCollapse< TriMeshType,VertexPair,MYTYPE>::GlobalMark(),_pp)));
00298 }
00299 }
00300 }
00301 }
00302 }
00303 static float HeapSimplexRatio(BaseParameterClass *_pp) {return IsSymmetric(_pp)?5.0f:9.0f;}
00304 static bool IsSymmetric(BaseParameterClass *_pp) {return ((QParameter *)_pp)->OptimalPlacement;}
00305 static bool IsVertexStable(BaseParameterClass *_pp) {return !((QParameter *)_pp)->OptimalPlacement;}
00306
00307
00315 ScalarType ComputePriority(BaseParameterClass *_pp)
00316 {
00317 QParameter *pp=(QParameter *)_pp;
00318 std::vector<CoordType> onVec;
00319 VertexType * v[2];
00320 v[0] = this->pos.V(0);
00321 v[1] = this->pos.V(1);
00322
00323 if(pp->NormalCheck){
00324
00325 for(VFIterator x(v[0]); !x.End(); ++x )
00326 if( x.V1()!=v[1] && x.V2()!=v[1] )
00327 onVec.push_back(TriangleNormal(*x.F()).Normalize());
00328
00329 for(VFIterator x(v[1]); !x.End(); ++x )
00330 if( x.V1()!=v[0] && x.V2()!=v[0] )
00331 onVec.push_back(TriangleNormal(*x.F()).Normalize());
00332 }
00333
00335 CoordType OldPos0=v[0]->P();
00336 CoordType OldPos1=v[1]->P();
00337 CoordType newPos = ComputePosition(_pp);
00338 v[0]->P() = v[1]->P() = newPos;
00339
00341 int i=0;
00342 double MinCos = std::numeric_limits<double>::max();
00343
00344
00345 double MinQual = std::numeric_limits<double>::max();
00346 for(VFIterator x(v[0]); !x.End(); ++x )
00347 if( x.V1()!=v[1] && x.V2()!=v[1] )
00348 {
00349 if(pp->NormalCheck){
00350 CoordType nn=NormalizedTriangleNormal(*x.F());
00351 double ndiff=nn.dot(onVec[i++]);
00352 MinCos=std::min(MinCos,ndiff);
00353 }
00354 if(pp->QualityCheck){
00355 double qt= QualityFace(*x.F());
00356 MinQual=std::min(MinQual,qt);
00357 }
00358 }
00359 for(VFIterator x(v[1]); !x.End(); ++x )
00360 if( x.V1()!=v[0] && x.V2()!=v[0] )
00361 {
00362 if(pp->NormalCheck){
00363 CoordType nn=NormalizedTriangleNormal(*x.F());
00364 double ndiff=nn.dot(onVec[i++]);
00365 MinCos=std::min(MinCos,ndiff);
00366 }
00367 if(pp->QualityCheck){
00368 double qt= QualityFace(*x.F());
00369 MinQual=std::min(MinQual,qt);
00370 }
00371 }
00372
00373 QuadricType qq=QH::Qd(v[0]);
00374 qq+=QH::Qd(v[1]);
00375
00376 double QuadErr = pp->ScaleFactor*qq.Apply(Point3d::Construct(v[1]->P()));
00377
00378 assert(!math::IsNAN(QuadErr));
00379
00380 if(MinQual>pp->QualityThr) MinQual=pp->QualityThr;
00381
00382 if(pp->NormalCheck){
00383
00384
00385 if(MinCos>pp->CosineThr) MinCos=pp->CosineThr;
00386 MinCos=fabs((MinCos+1)/2.0);
00387 }
00388
00389 QuadErr= std::max(QuadErr,pp->QuadricEpsilon);
00390 if(QuadErr <= pp->QuadricEpsilon)
00391 {
00392 QuadErr = - 1/Distance(OldPos0,OldPos1);
00393 }
00394
00395 if( pp->UseVertexWeight ) QuadErr *= (QH::W(v[1])+QH::W(v[0]))/2;
00396
00397 ScalarType error;
00398 if(!pp->QualityCheck && !pp->NormalCheck) error = (ScalarType)(QuadErr);
00399 if( pp->QualityCheck && !pp->NormalCheck) error = (ScalarType)(QuadErr / MinQual);
00400 if(!pp->QualityCheck && pp->NormalCheck) error = (ScalarType)(QuadErr / MinCos);
00401 if( pp->QualityCheck && pp->NormalCheck) error = (ScalarType)(QuadErr / (MinQual*MinCos));
00402
00403
00404 v[0]->P()=OldPos0;
00405 v[1]->P()=OldPos1;
00406
00407 this->_priority = error;
00408 return this->_priority;
00409 }
00410
00411
00412
00413
00414 inline void AddCollapseToHeap(HeapType & h_ret, VertexType *v0, VertexType *v1, BaseParameterClass *_pp)
00415 {
00416 QParameter *pp=(QParameter *)_pp;
00417 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(v0,v1), this->GlobalMark(),_pp)));
00418 std::push_heap(h_ret.begin(),h_ret.end());
00419 if(!IsSymmetric(pp)){
00420 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(v1,v0), this->GlobalMark(),_pp)));
00421 std::push_heap(h_ret.begin(),h_ret.end());
00422 }
00423 }
00424
00425 inline void UpdateHeap(HeapType & h_ret, BaseParameterClass *_pp)
00426 {
00427 this->GlobalMark()++;
00428 VertexType *v[2];
00429 v[0]= this->pos.V(0);
00430 v[1]= this->pos.V(1);
00431 v[1]->IMark() = this->GlobalMark();
00432
00433
00434 for(VFIterator vfi(v[1]); !vfi.End(); ++vfi ) {
00435 vfi.V1()->ClearV();
00436 vfi.V2()->ClearV();
00437 }
00438
00439
00440 for(VFIterator vfi(v[1]); !vfi.End(); ++vfi ) {
00441 if( !(vfi.V1()->IsV()) && vfi.V1()->IsRW())
00442 {
00443 vfi.V1()->SetV();
00444 AddCollapseToHeap(h_ret,vfi.V0(),vfi.V1(),_pp);
00445 }
00446 if( !(vfi.V2()->IsV()) && vfi.V2()->IsRW())
00447 {
00448 vfi.V2()->SetV();
00449 AddCollapseToHeap(h_ret,vfi.V2(),vfi.V0(),_pp);
00450 }
00451 if(vfi.V1()->IsRW() && vfi.V2()->IsRW() )
00452 AddCollapseToHeap(h_ret,vfi.V1(),vfi.V2(),_pp);
00453 }
00454 }
00455
00456 static void InitQuadric(TriMeshType &m,BaseParameterClass *_pp)
00457 {
00458 QParameter *pp=(QParameter *)_pp;
00459 QH::Init();
00460
00461 for(VertexIterator pv=m.vert.begin();pv!=m.vert.end();++pv)
00462 if( ! (*pv).IsD() && (*pv).IsW())
00463 QH::Qd(*pv).SetZero();
00464
00465 for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
00466 if( !(*fi).IsD() && (*fi).IsR() )
00467 if((*fi).V(0)->IsR() &&(*fi).V(1)->IsR() &&(*fi).V(2)->IsR())
00468 {
00469 Plane3<ScalarType,false> facePlane;
00470 facePlane.SetDirection( ( (*fi).V(1)->cP() - (*fi).V(0)->cP() ) ^ ( (*fi).V(2)->cP() - (*fi).V(0)->cP() ));
00471 if(!pp->UseArea)
00472 facePlane.Normalize();
00473 facePlane.SetOffset( facePlane.Direction().dot((*fi).V(0)->cP()));
00474
00475 QuadricType q;
00476 q.ByPlane(facePlane);
00477
00478
00479 for(int j=0;j<3;++j)
00480 if( (*fi).V(j)->IsW() )
00481 QH::Qd((*fi).V(j)) += q;
00482
00483 for(int j=0;j<3;++j)
00484 if( (*fi).IsB(j) || pp->QualityQuadric )
00485 {
00486 Plane3<ScalarType,false> borderPlane;
00487 QuadricType bq;
00488
00489
00490 borderPlane.SetDirection(facePlane.Direction() ^ ( (*fi).V1(j)->cP() - (*fi).V(j)->cP() ).normalized());
00491 if( (*fi).IsB(j) ) borderPlane.SetDirection(borderPlane.Direction()* (ScalarType)(pp->BoundaryWeight ));
00492 else borderPlane.SetDirection(borderPlane.Direction()* (ScalarType)(pp->BoundaryWeight/100.0));
00493 borderPlane.SetOffset(borderPlane.Direction().dot((*fi).V(j)->cP()));
00494 bq.ByPlane(borderPlane);
00495
00496 if( (*fi).V (j)->IsW() ) QH::Qd((*fi).V (j)) += bq;
00497 if( (*fi).V1(j)->IsW() ) QH::Qd((*fi).V1(j)) += bq;
00498 }
00499 }
00500
00501 if(pp->ScaleIndependent)
00502 {
00503 vcg::tri::UpdateBounding<TriMeshType>::Box(m);
00504
00505 pp->ScaleFactor = 1e8*pow(1.0/m.bbox.Diag(),6);
00506 }
00507
00508 if(pp->QualityWeight)
00509 {
00510 float minQ, maxQ;
00511 tri::Stat<TriMeshType>::ComputePerVertexQualityMinMax(m,minQ,maxQ);
00512 for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
00513 if( ! (*vi).IsD() && (*vi).IsW())
00514 {
00515 const double quality01squared = pow((vi->Q()-minQ)/(maxQ-minQ),2.0);
00516 QH::Qd(*vi) *= 1.0 + quality01squared * (pp->QualityWeightFactor-1.0);
00517 }
00518 }
00519 }
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536 CoordType ComputeMinimal()
00537 {
00538 typename TriMeshType::VertexType * v[2];
00539 v[0] = this->pos.V(0);
00540 v[1] = this->pos.V(1);
00541 QuadricType q=QH::Qd(v[0]);
00542 q+=QH::Qd(v[1]);
00543
00544 Point3<QuadricType::ScalarType> x;
00545
00546 bool rt=q.Minimum(x);
00547 if(!rt) {
00548 Point3<QuadricType::ScalarType> x0=Point3d::Construct(v[0]->P());
00549 Point3<QuadricType::ScalarType> x1=Point3d::Construct(v[1]->P());
00550 x.Import((v[0]->P()+v[1]->P())/2);
00551 double qvx=q.Apply(x);
00552 double qv0=q.Apply(x0);
00553 double qv1=q.Apply(x1);
00554 if(qv0<qvx) x=x0;
00555 if(qv1<qvx && qv1<qv0) x=x1;
00556 }
00557
00558 return CoordType::Construct(x);
00559 }
00560
00561
00562
00563 };
00564 }
00565 }
00566 #endif