tri_edge_collapse_quadric.h
Go to the documentation of this file.
00001 /****************************************************************************
00002 * VCGLib                                                            o o     *
00003 * Visual and Computer Graphics Library                            o     o   *
00004 *                                                                _   O  _   *
00005 * Copyright(C) 2004                                                \/)\/    *
00006 * Visual Computing Lab                                            /\/|      *
00007 * ISTI - Italian National Research Council                           |      *
00008 *                                                                    \      *
00009 * All rights reserved.                                                      *
00010 *                                                                           *
00011 * This program is free software; you can redistribute it and/or modify      *
00012 * it under the terms of the GNU General Public License as published by      *
00013 * the Free Software Foundation; either version 2 of the License, or         *
00014 * (at your option) any later version.                                       *
00015 *                                                                           *
00016 * This program is distributed in the hope that it will be useful,           *
00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
00019 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
00020 * for more details.                                                         *
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         //**Helper CLASSES**//
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 */*v*/) {return 1.0;}
00082       static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE &/*v*/) {return 1.0;}
00083       static void Merge(VERTEX_TYPE & /*v_dest*/, VERTEX_TYPE const & /*v_del*/){}
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; // During the initialization manage all the edges as border edges adding a set of additional quadrics that are useful mostly for keeping face aspect ratio good.
00101   double    QualityThr;     // Collapsed that generate faces with quality LOWER than this value are penalized. So 
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;        // higher the value -> better the quality of the accepted triangles
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   // puntatori ai vertici che sono stati messi non-w per preservare il boundary
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)); // v0 is deleted and v1 take the new position
00195   }
00196 
00197 
00198 
00199     // Final Clean up after the end of the simplification process
00200     static void Finalize(TriMeshType &m, HeapType& /*h_ret*/, BaseParameterClass *_pp)
00201     {
00202       QParameter *pp=(QParameter *)_pp;
00203 
00204       // If we had the boundary preservation we should clean up the writable flags
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   // Initialize the heap with all the possible collapses
00259     if(IsSymmetric(pp))
00260     { // if the collapse is symmetric (e.g. u->v == v->u)
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     { // if the collapse is A-symmetric (e.g. u->v != v->u)
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; // vector with incident faces original normals 
00319     VertexType * v[2];
00320     v[0] = this->pos.V(0);
00321     v[1] = this->pos.V(1);
00322     
00323     if(pp->NormalCheck){ // Compute maximal normal variation
00324       // store the old normals for non-collapsed face in v0
00325       for(VFIterator x(v[0]); !x.End(); ++x )    // for all faces in v0
00326         if( x.V1()!=v[1] && x.V2()!=v[1] ) // skip faces with v1
00327           onVec.push_back(TriangleNormal(*x.F()).Normalize());
00328       // store the old normals for non-collapsed face in v1
00329       for(VFIterator x(v[1]); !x.End(); ++x )    // for all faces in v1
00330         if( x.V1()!=v[0] && x.V2()!=v[0] ) // skip faces with v0
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(); // minimo coseno di variazione di una normale della faccia
00343     // (e.g. max angle) Mincos varia da 1 (normali coincidenti) a
00344     // -1 (normali opposte);
00345     double MinQual = std::numeric_limits<double>::max();
00346     for(VFIterator x(v[0]); !x.End(); ++x )  // for all faces in v0
00347       if( x.V1()!=v[1] && x.V2()!=v[1] )     // skiping faces with v1
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 )      // for all faces in v1
00360       if( x.V1()!=v[0] && x.V2()!=v[0] ) // skip faces with v0
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     // All collapses involving triangles with quality larger than <QualityThr> have no penalty;
00380     if(MinQual>pp->QualityThr) MinQual=pp->QualityThr;
00381     
00382     if(pp->NormalCheck){     
00383       // All collapses where the normal vary less than <NormalThr> (e.g. more than CosineThr)
00384       // have no penalty
00385       if(MinCos>pp->CosineThr) MinCos=pp->CosineThr;
00386       MinCos=fabs((MinCos+1)/2.0); // Now it is in the range 0..1 with 0 very dangerous!
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     // Restore old position of v0 and v1
00404     v[0]->P()=OldPos0;
00405     v[1]->P()=OldPos1;
00406     
00407     this->_priority = error;
00408     return this->_priority;
00409   }
00410   
00411 //
00412 //static double MaxError() {return 1e100;}
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     // First loop around the surviving vertex to unmark the Visit flags
00434     for(VFIterator vfi(v[1]); !vfi.End(); ++vfi ) {
00435       vfi.V1()->ClearV();
00436       vfi.V2()->ClearV();
00437     }
00438 
00439     // Second Loop
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     } // end second loop around surviving vertex.
00454   }
00455 
00456   static void InitQuadric(TriMeshType &m,BaseParameterClass *_pp)
00457   {
00458     QParameter *pp=(QParameter *)_pp;
00459     QH::Init();
00460     //  m.ClearFlags();
00461     for(VertexIterator pv=m.vert.begin();pv!=m.vert.end();++pv)         // Azzero le quadriche
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           // The basic < add face quadric to each vertex > loop
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               // Border quadric record the squared distance from the plane orthogonal to the face and passing 
00489               // through the edge. 
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 ));        // amplify border planes
00492               else                borderPlane.SetDirection(borderPlane.Direction()* (ScalarType)(pp->BoundaryWeight/100.0));   // and consider much less quadric for quality
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       //Make all quadric independent from mesh size
00505       pp->ScaleFactor = 1e8*pow(1.0/m.bbox.Diag(),6); // scaling factor
00506     }
00507 
00508     if(pp->QualityWeight) // we map quality range into a squared 01 and than this into the 1..QualityWeightFactor range
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 //static void InitMesh(MESH_TYPE &m){
00530 //      pp->CosineThr=cos(pp->NormalThr);
00531 //  InitQuadric(m);
00532 //      //m.Topology();
00533 //      //OldInitQuadric(m,UseArea);
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) { // if the computation of the minimum fails we choose between the two edge points and the middle one.
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         } // namespace tri
00565     } // namespace vcg
00566 #endif


shape_reconstruction
Author(s): Roberto Martín-Martín
autogenerated on Sat Jun 8 2019 18:37:44