/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef __VCGLIB_POLY_MESH_ALGORITHM #define __VCGLIB_POLY_MESH_ALGORITHM #include #include #include #include #include #include #include #include //define a temporary triangle mesh type class TempFace; class TempVertex; struct TempUsedTypes: public vcg::UsedTypes::AsVertexType, vcg::Use::AsFaceType>{}; class TempVertex:public vcg::Vertex {}; class TempFace:public vcg::Face {}; class TempMesh: public vcg::tri::TriMesh< std::vector,std::vector > {}; namespace vcg{ /*! \ingroup PolyMeshType \headerfile color.h vcg/complex/algorithms/polygonal_algorithms.h \brief processing and optimization of generic polygonal meshes. This class is used to performs varisous kind of geometric optimization on generic polygonal mesh such as flattengin or imptove the shape of polygons. */ template class PolygonalAlgorithm { typedef typename PolyMeshType::FaceType FaceType; typedef typename PolyMeshType::VertexType VertexType; typedef typename PolyMeshType::VertexPointer VertexPointer; typedef typename PolyMeshType::CoordType CoordType; typedef typename PolyMeshType::ScalarType ScalarType; typedef typename vcg::face::Pos PosType; static void SetFacePos(PolyMeshType &poly_m, int IndexF,std::vector &Pos) { poly_m.face[IndexF].Dealloc(); poly_m.face[IndexF].Alloc(Pos.size()); //std::cout<::AddVertex(poly_m,Pos[i]); for (size_t i=0;i Bary; for (size_t i=0;i, CoordType> EdgeVert; for (size_t i=0;i Key(std::min(Pos0,Pos1),std::max(Pos0,Pos1)); EdgeVert[Key]=Avg; } int sizeF=poly_m.face.size(); for (size_t i=0;i Pos; for (size_t j=0;j Key0(std::min(Pos0,Pos1),std::max(Pos0,Pos1)); Pos0=EdgeVert[Key0]; Pos.push_back(Pos0); Pos.push_back(Pos1); } //get also the barycenter CoordType BaryP=Bary[i]; //then retrieve the face std::vector PosQ; PosQ.push_back(Pos[0]); PosQ.push_back(Pos[1]); PosQ.push_back(Pos[2]); PosQ.push_back(BaryP); SetFacePos(poly_m,i,PosQ); int sizeV=Pos.size(); //int start=0; for (size_t j=2;j::AddFaces(poly_m,1); std::vector PosQ; PosQ.push_back(Pos[(j)%Pos.size()]); PosQ.push_back(Pos[(j+1)%Pos.size()]); PosQ.push_back(Pos[(j+2)%Pos.size()]); PosQ.push_back(BaryP); //start+=2; SetFacePos(poly_m,poly_m.face.size()-1,PosQ); //break; } } vcg::tri::Clean::RemoveDuplicateVertex(poly_m); vcg::tri::Allocator::CompactEveryVector(poly_m); } static bool CollapseEdges(PolyMeshType &poly_m, const std::vector &CollapsePos, const std::vector &InterpPos) { //this set how to remap the vertices after deletion std::map VertexRemap; vcg::tri::UpdateFlags::VertexClearS(poly_m); bool collapsed=false; //go over all faces and check the ones needed to be deleted for (size_t i=0;iVN(); VertexType *v0=currF->V(IndexE); VertexType *v1=currF->V((IndexE+1)%NumV); //safety check assert(v0!=v1); if (v0->IsS())continue; if (v1->IsS())continue; //put on the same position v0->P()=InterpPos[i]; v1->P()=InterpPos[i]; //select the the two vertices v0->SetS(); v1->SetS(); //set the remap VertexRemap[v1]=v0; collapsed=true; } //then remap vertices for (size_t i=0;i FaceV; for (int j=0;j::RemoveUnreferencedVertex(poly_m); //and compact them vcg::tri::Allocator::CompactEveryVector(poly_m); return collapsed; } private: static bool CollapseBorderSmallEdgesStep(PolyMeshType &poly_m, const ScalarType edge_limit) { //update topology vcg::tri::UpdateTopology::FaceFace(poly_m); //update border vertices vcg::tri::UpdateFlags::VertexBorderFromFaceAdj(poly_m); vcg::tri::UpdateSelection::VertexCornerBorder(poly_m,math::ToRad(150.0)); std::vector CollapsePos; std::vector InterpPos; //go over all faces and check the ones needed to be deleted for (size_t i=0;iIsB(); bool IsBV1=v1->IsB(); bool IsS0=v0->IsS(); bool IsS1=v1->IsS(); if ((IsS0)&&(IsS1))continue; //in these cases is not possible to collapse if ((!IsBV0)&&(!IsBV1))continue; bool IsBorderE=(poly_m.face[i].FFp(j)==&poly_m.face[i]); if ((!IsBorderE)&&(IsBV0)&&(IsBV1))continue; assert((IsBV0)||(IsBV1)); CoordType pos0=v0->P(); CoordType pos1=v1->P(); ScalarType currL=(pos0-pos1).Norm(); if (currL>edge_limit)continue; //then collapse the point CoordType CurrInterpPos; if ((IsBV0)&&(!IsBV1))CurrInterpPos=pos0; if ((!IsBV0)&&(IsBV1))CurrInterpPos=pos1; if ((IsBV0)&&(IsBV1)) { if ((!IsS0)&&(!IsS1)) CurrInterpPos=(pos0+pos1)/2.0; else { if ((!IsS0)&&(IsS1)) CurrInterpPos=pos1; else { assert((IsS0)&&(!IsS1)); CurrInterpPos=pos0; } } } CollapsePos.push_back(PosType(&poly_m.face[i],j)); InterpPos.push_back(CurrInterpPos); } } return CollapseEdges(poly_m,CollapsePos,InterpPos); } static void LaplacianPos(PolyMeshType &poly_m,std::vector &AvVert) { //cumulate step AvVert.clear(); AvVert.resize(poly_m.vert.size(),CoordType(0,0,0)); std::vector AvSum(poly_m.vert.size(),0); for (size_t i=0;iP(); //cumulate over other positions ScalarType W=vcg::PolyArea(poly_m.face[i]); //assert(W!=0); for (size_t k=0;k<(size_t)poly_m.face[i].VN();k++) { if (k==j) continue; int IndexV=vcg::tri::Index(poly_m,poly_m.face[i].V(k)); AvVert[IndexV]+=currP*W; AvSum[IndexV]+=W; } } //average step for (size_t i=0;i PlF; PlF=PolyFittingPlane(F); if ((PlF.Direction()*F.N())<0) F.N()=-PlF.Direction(); else F.N()=PlF.Direction(); } public: static void SelectIrregularInternal(PolyMeshType &poly_m) { vcg::tri::UpdateQuality::VertexValence(poly_m); vcg::tri::UpdateSelection::VertexClear(poly_m); for (size_t i=0;i::VertexValence(poly_m); for (size_t i=0;i &TemplatePos) { vcg::GetPolyTemplatePos(f,TemplatePos,true); CoordType NormT=Normal(TemplatePos); //get the normal of vertices //CoordType AVN(0,0,0); //CoordType AVN0(0,0,0); CoordType Origin(0,0,0); // for (int j=0;jN(); CoordType AVN=vcg::PolygonNormal(f); //AVN0.Normalize(); // std::cout<<"AVN "< Rot=vcg::RotationMatrix(NormT,AVN); //apply transformation for (size_t j=0;j *IgnoreF=NULL) { (void)isotropic; typedef typename PolyMeshType::FaceType PolygonType; // // select irregular ones // if (fixIrr) // poly_m.NumIrregular(true); // compute the average edge ScalarType MeshArea=0; for (size_t i=0;i avgPos(poly_m.vert.size(),CoordType(0,0,0)); std::vector weightSum(poly_m.vert.size(),0); //then compute the templated positions for (size_t i=0;i TemplatePos; GetRotatedTemplatePos(poly_m.face[i],TemplatePos); //then cumulate the position per vertex ScalarType val=vcg::PolyArea(poly_m.face[i]); if (val<(AvgArea*0.00001)) val=(AvgArea*0.00001); ScalarType W=1.0/val; if (WeightByQuality) W=poly_m.face[i].Q()+0.00001; for (size_t j=0;j AvVert; LaplacianPos(poly_m,AvVert); //then update the position for (size_t i=0;i1)alpha=1; // if (isnan(alpha))alpha=1; CoordType newP=poly_m.vert[i].P(); //safety checks if (weightSum[i]>0) newP=avgPos[i]/weightSum[i]; if (isnan(newP.X())||isnan(newP.Y())||isnan(newP.Z())) newP=poly_m.vert[i].P(); if ((newP-poly_m.vert[i].P()).Norm()>poly_m.bbox.Diag()) newP=poly_m.vert[i].P(); //std::cout<<"W "< static void ReprojectBorder(PolyMeshType &poly_m, TriMeshType &tri_mesh, bool FixS=true) { //then reproject on border for (size_t i=0;i::max(); CoordType closPos; for (size_t j=0;j Seg(P0,P1); ScalarType testD; CoordType closTest; vcg::SegmentPointDistance(Seg,testPos,closTest,testD); if (testD>minD)continue; minD=testD; closPos=closTest; } poly_m.vert[i].P()=closPos; } } /*! \brief This function smooth the borders of the polygonal mesh and reproject back to the triangolar one * except the vertices that are considered as corner wrt the angleDeg threshold */ template static void LaplacianReprojectBorder(PolyMeshType &poly_m, TriMeshType &tri_mesh, int nstep=100, ScalarType Damp=0.5, ScalarType angleDeg=100) { //first select corners vcg::tri::UpdateFlags::VertexClearS(poly_m); //update topology vcg::tri::UpdateTopology::FaceFace(poly_m); //update border vertices vcg::tri::UpdateFlags::VertexBorderFromFaceAdj(poly_m); //select corner vertices on the border ScalarType angleRad=angleDeg * M_PI / 180; vcg::tri::UpdateSelection::VertexCornerBorder(poly_m,angleRad); for (int s=0;s AvVert; LaplacianPos(poly_m,AvVert); for (size_t i=0;i::max(); // CoordType closPos; // for (size_t j=0;j Seg(P0,P1); // ScalarType testD; // CoordType closTest; // vcg::SegmentPointDistance(Seg,testPos,closTest,testD); // if (testD>minD)continue; // minD=testD; // closPos=closTest; // } // poly_m.vert[i].P()=closPos; // } ReprojectBorder(poly_m,tri_mesh); } } /*! \brief This function smooth the borders of the polygonal mesh and reproject back to its border */ static void LaplacianReprojectBorder(PolyMeshType &poly_m, int nstep=100, ScalarType Damp=0.5, ScalarType Angle=100) { //transform into triangular TempMesh GuideSurf; vcg::tri::PolygonSupport::ImportFromPolyMesh(GuideSurf,poly_m); vcg::tri::UpdateBounding::Box(GuideSurf); vcg::tri::UpdateNormal::PerVertexNormalizedPerFace(GuideSurf); vcg::tri::UpdateTopology::FaceFace(GuideSurf); vcg::tri::UpdateFlags::FaceBorderFromFF(GuideSurf); LaplacianReprojectBorder(poly_m,GuideSurf,nstep,Damp,Angle); } /*! \brief This function performs the reprojection of the polygonal mesh onto a triangular one passed as input parameter */ template static void LaplacianReproject(PolyMeshType &poly_m, TriMeshType &tri_mesh, int nstep=100, ScalarType DampS=0.5, ScalarType DampR=0.5, bool OnlyOnSelected=false) { typedef typename TriMeshType::FaceType TriFaceType; typedef typename TriMeshType::ScalarType TriScalarType; typedef typename TriMeshType::CoordType TriCoordType; typedef vcg::GridStaticPtr TriMeshGrid; TriMeshGrid grid; tri::MeshAssert::VertexNormalNormalized(tri_mesh); //initialize the grid grid.Set(tri_mesh.face.begin(),tri_mesh.face.end()); TriScalarType MaxD=tri_mesh.bbox.Diag(); for (int s=0;s AvVert; LaplacianPos(poly_m,AvVert); for (size_t i=0;i:(GuideSurf,poly_m); TriangulateToTriMesh(poly_m,GuideSurf); vcg::tri::UpdateBounding::Box(GuideSurf); vcg::tri::UpdateNormal::PerVertexNormalizedPerFace(GuideSurf); vcg::tri::UpdateTopology::FaceFace(GuideSurf); vcg::tri::UpdateFlags::FaceBorderFromFF(GuideSurf); LaplacianReproject(poly_m,GuideSurf,nstep,Damp,0.5,OnlyOnSelected); } static void Laplacian(PolyMeshType &poly_m, bool FixS=false, int nstep=10, ScalarType Damp=0.5) { for (int s=0;s AvVert; LaplacianPos(poly_m,AvVert); for (size_t i=0;i static void SmoothReprojectPCA(PolyMeshType &poly_m, TriMeshType &tri_mesh, int relaxStep=100, bool fixS=false, ScalarType Damp=0.5, ScalarType SharpDeg=0, bool WeightByQuality=false, bool FixB=true) { //vcg::tri::UpdateFlags::VertexClearS(poly_m); vcg::tri::UpdateTopology::FaceFace(poly_m); //UpdateBorderVertexFromPFFAdj(poly_m); vcg::tri::UpdateFlags::VertexBorderFromFaceAdj(poly_m); std::vector > > SharpEdge(poly_m.vert.size()); //first select sharp features if (SharpDeg>0) { for (int i=0;i<(int)poly_m.face.size();i++) for (int j=0;j<(int)poly_m.face[i].VN();j++) { //check only one side if ((&poly_m.face[i])>=poly_m.face[i].FFp(j))continue; CoordType N0=poly_m.face[i].N(); CoordType N1=poly_m.face[i].FFp(j)->N(); ScalarType Angle=vcg::Angle(N0,N1); if (fabs(Angle)>(SharpDeg* (M_PI / 180.0))) { CoordType Pos0=poly_m.face[i].V0(j)->P(); CoordType Pos1=poly_m.face[i].V1(j)->P(); CoordType Ori=Pos0; CoordType Dir=Pos1-Pos0; Dir.Normalize(); vcg::Line3 L(Ori,Dir); int Index0=vcg::tri::Index(poly_m,poly_m.face[i].V0(j)); int Index1=vcg::tri::Index(poly_m,poly_m.face[i].V1(j)); SharpEdge[Index0].push_back(L); SharpEdge[Index1].push_back(L); } } for (size_t i=0;i2)poly_m.vert[i].SetS(); } } // if (fixIrr) // { // vcg::tri::UpdateQuality::VertexValence(poly_m); // for (size_t i=0;i TriMeshGrid; TriMeshGrid grid; //initialize the grid grid.Set(tri_mesh.face.begin(),tri_mesh.face.end()); ScalarType MaxD=tri_mesh.bbox.Diag(); // //update quality as area // for (size_t i=0;i0); poly_m.vert[i].P()=av_closest/sum; } } if (!FixB) ReprojectBorder(poly_m,tri_mesh,true); UpdateFaceNormals(poly_m); vcg::tri::UpdateNormal::PerVertexFromCurrentFaceNormal(poly_m); } } template static void TriangulateToTriMesh(PolyMeshType &poly_m,TriMeshType &triangle_mesh, bool alsoTriangles = true) { triangle_mesh.Clear(); PolyMeshType PolySwap; vcg::tri::Append::Mesh(PolySwap,poly_m); Triangulate(PolySwap, alsoTriangles); //then copy onto the triangle mesh vcg::tri::Append::Mesh(triangle_mesh,PolySwap); } /*! \brief This function performs the polygon regularization as in "Statics Aware Grid Shells" * followed by a reprojection step on the original mesh */ static void SmoothReprojectPCA(PolyMeshType &poly_m, int relaxStep=100, bool fixS=false, ScalarType Damp=0.5, ScalarType SharpDeg=0, bool WeightByQuality=false, bool FixB=true) { //transform into triangular TempMesh GuideSurf; //vcg::tri::PolygonSupport:(GuideSurf,poly_m); TriangulateToTriMesh(poly_m,GuideSurf); vcg::tri::UpdateBounding::Box(GuideSurf); vcg::tri::UpdateNormal::PerVertexNormalizedPerFace(GuideSurf); vcg::tri::UpdateTopology::FaceFace(GuideSurf); vcg::tri::UpdateFlags::FaceBorderFromFF(GuideSurf); //optimize it vcg::PolygonalAlgorithm::SmoothReprojectPCA(poly_m,GuideSurf,relaxStep,fixS,Damp,SharpDeg,WeightByQuality,FixB); } static void Reproject(PolyMeshType &poly_m, PolyMeshType &target) { vcg::tri::UpdateTopology::FaceFace(poly_m); vcg::tri::UpdateFlags::VertexBorderFromFaceAdj(poly_m); //transform into triangular TempMesh GuideSurf; //vcg::tri::PolygonSupport:(GuideSurf,poly_m); TriangulateToTriMesh(target,GuideSurf); vcg::tri::UpdateBounding::Box(GuideSurf); vcg::tri::UpdateNormal::PerVertexNormalizedPerFace(GuideSurf); vcg::tri::UpdateTopology::FaceFace(GuideSurf); vcg::tri::UpdateFlags::FaceBorderFromFF(GuideSurf); //initialize the grid typedef typename TempMesh::FaceType FaceType; typedef vcg::GridStaticPtr TriMeshGrid; TriMeshGrid grid; grid.Set(GuideSurf.face.begin(),GuideSurf.face.end()); ScalarType MaxD=GuideSurf.bbox.Diag(); for (size_t i=0;i static void ReprojectonTriMesh(PolyMeshType &poly_m, TriMesh &target) { vcg::tri::UpdateTopology::FaceFace(poly_m); vcg::tri::UpdateFlags::VertexBorderFromFaceAdj(poly_m); //initialize the grid typedef typename TriMesh::FaceType FaceType; typedef vcg::GridStaticPtr TriMeshGrid; TriMeshGrid grid; grid.Set(target.face.begin(),target.face.end()); ScalarType MaxD=target.bbox.Diag(); for (size_t i=0;iP(); CoordType pos1=poly_m.face[i].cV((j+1)%NumV)->P(); AvL+=(pos0-pos1).Norm(); numE++; } } AvL/=numE; return AvL; } /*! \brief This function remove valence 2 faces from the mesh */ static void RemoveValence2Faces(PolyMeshType &poly_m) { for (size_t i=0;i=3)continue; vcg::tri::Allocator::DeleteFace(poly_m,poly_m.face[i]); } //then remove unreferenced vertices vcg::tri::Clean::RemoveUnreferencedVertex(poly_m); vcg::tri::Allocator::CompactEveryVector(poly_m); } /*! \brief This function remove valence 2 vertices on the border by considering the degree threshold * bacause there could be eventually some corner that should be preserved */ static void RemoveValence2Vertices(PolyMeshType &poly_m, ScalarType corner_degree=25) { //update topology vcg::tri::UpdateTopology::FaceFace(poly_m); //update border vertices //UpdateBorderVertexFromPFFAdj(poly_m); vcg::tri::UpdateFlags::VertexBorderFromFaceAdj(poly_m); vcg::tri::UpdateFlags::VertexClearS(poly_m); //select corners for (size_t i=0;iIsB())&&(v1->IsB())&&(v2->IsB())); CoordType dir0=(v0->P()-v1->P()); CoordType dir1=(v2->P()-v1->P()); dir0.Normalize(); dir1.Normalize(); ScalarType testDot=(dir0*dir1); if ((IsB)&&(testDot>(-cos(corner_degree* (M_PI / 180.0))))) v1->SetS(); } } typename PolyMeshType::template PerVertexAttributeHandle valenceVertH = vcg::tri::Allocator:: template GetPerVertexAttribute (poly_m); //initialize to zero for (size_t i=0;i FaceV; for (int j=0;jIsD()); //if ((!v->IsS()) && (v->IsB()) && (valenceVertH[v]==1)) continue; if ((!v->IsS()) && (v->IsB()) && (valenceVertH[v]==1)) continue; if ((!v->IsB()) && (valenceVertH[v]<3)) continue; //if (!v->IsS()) continue; FaceV.push_back(v); } //then deallocate face if ((int)FaceV.size()==NumV)continue; //otherwise deallocate and set new vertices poly_m.face[i].Dealloc(); poly_m.face[i].Alloc(FaceV.size()); for (size_t j=0;j::RemoveUnreferencedVertex(poly_m); vcg::tri::Allocator::CompactEveryVector(poly_m); vcg::tri::Allocator::DeletePerVertexAttribute(poly_m,valenceVertH); } /*! \brief This function collapse small edges which are on the boundary of the mesh * this is sometimes useful to remove small edges coming out from a quadrangulation which is not * aligned to boundaries */ static bool CollapseBorderSmallEdges(PolyMeshType &poly_m, const ScalarType perc_average=0.3) { //compute the average edge ScalarType AvEdge=AverageEdge(poly_m); ScalarType minLimit=AvEdge*perc_average; bool collapsed=false; while(CollapseBorderSmallEdgesStep(poly_m,minLimit)){collapsed=true;}; RemoveValence2Faces(poly_m); //RemoveValence2BorderVertices(poly_m); RemoveValence2Vertices(poly_m); return collapsed; } /*! \brief This function use a local global approach to flatten polygonal faces * the approach is similar to "Shape-Up: Shaping Discrete Geometry with Projections" */ static ScalarType FlattenFaces(PolyMeshType &poly_m, size_t steps=100,bool OnlySFaces=false) { ScalarType MaxDispl=0; for (size_t s=0;s > VertPos(poly_m.vert.size()); for (size_t i=0;i FacePos; for (int j=0;jIsD()); FacePos.push_back(v->P()); } //then fit the plane vcg::Plane3 FitPl; vcg::FitPlaneToPointSet(FacePos,FitPl); //project each point onto fitting plane for (int j=0;jP()); VertPos[IndexV].push_back(ProjP); } } for (size_t i=0;i T0(P1,(P0+P1)/2,baryF); vcg::Triangle3 T1(P1,(P1+P2)/2,baryF); poly_m.face[i].V(j)->Q()+=vcg::DoubleArea(T0)/2; poly_m.face[i].V(j)->Q()+=vcg::DoubleArea(T1)/2; } } } static ScalarType InitQualityFaceTorsion(PolyMeshType &poly_m) { UpdateFaceNormalByFitting(poly_m); vcg::tri::UpdateNormal::PerVertexFromCurrentFaceNormal(poly_m); ScalarType MaxA=0; for (size_t i=0;i::PerVertexFromCurrentFaceNormal(poly_m); ScalarType MaxA=0; for (size_t i=0;iFFp(j); if (f>f1)continue; ScalarType L=(poly_m.face[i].P0(j)-poly_m.face[i].P1(j)).Norm(); poly_m.face[i].V0(j)->Q()+=L; poly_m.face[i].V1(j)->Q()+=L; } } } static void InterpolateQualityVertFormFaces(PolyMeshType &poly_m) { std::vector SumW(poly_m.vert.size(),0); for (size_t i=0;iQ()+=AreaF*(ScalarType)poly_m.face[i].Q(); size_t IndexV=vcg::tri::Index(poly_m,poly_m.face[i].V(j)); SumW[IndexV]+=AreaF; } } for (size_t i=0;i0) poly_m.vert[i].Q()/=SumW[i]; else poly_m.vert[i].Q()=0; } } static void ClosestPoint(const PolyMeshType &poly_m,const CoordType &pos, int &CloseF,CoordType &ClosePos) { ScalarType minD=std::numeric_limits::max(); CloseF=-1; for (size_t i=0;iminD)continue; minD=currD; CloseF=i; ClosePos=closeTest; } } /*! \brief Triangulate a polygonal face with a triangle fan. * \returns pointer to the newly added vertex. */ static VertexPointer Triangulate(PolyMeshType & poly_m, size_t IndexF) { const CoordType bary = vcg::PolyBarycenter(poly_m.face[IndexF]); size_t sizeV = poly_m.face[IndexF].VN(); //add the new vertex VertexPointer newV = &(*vcg::tri::Allocator::AddVertex(poly_m,bary)); //then reupdate the faces for (size_t j=0;j<(sizeV-1);j++) { VertexType * v0=poly_m.face[IndexF].V0(j); VertexType * v1=poly_m.face[IndexF].V1(j); VertexType * v2=newV; vcg::tri::Allocator::AddFaces(poly_m,1); poly_m.face.back().Alloc(3); poly_m.face.back().V(0)=v0; poly_m.face.back().V(1)=v1; poly_m.face.back().V(2)=v2; poly_m.face.back().Q()=poly_m.face[IndexF].Q(); } VertexType * v0=poly_m.face[IndexF].V0((sizeV-1)); VertexType * v1=poly_m.face[IndexF].V1((sizeV-1)); poly_m.face[IndexF].Dealloc(); poly_m.face[IndexF].Alloc(3); poly_m.face[IndexF].V(0)=v0; poly_m.face[IndexF].V(1)=v1; poly_m.face[IndexF].V(2)=newV; return newV; } static void ReorderFaceVert(FaceType &f,const size_t &StartI) { if (StartI==0)return; size_t sizeN=f.VN(); assert(StartI>=0); assert(StartI NewV; for (size_t i=0;iVN(); ReorderFaceVert(*f1,FirstV1); std::vector NewV; for (size_t i=0;i<(f.VN()-1);i++) NewV.push_back(f.V(i)); for (size_t i=0;i<(f1->VN()-1);i++) NewV.push_back(f1->V(i)); f.Dealloc(); f.Alloc(NewV.size()); for (size_t i=0;i::DeleteFace(poly_m,*f1); } static void MergeAlongEdges(PolyMeshType &poly_m, const std::vector &PolyF, const std::vector &EdgeI) { //create a table with all edges that have to be merged std::set > NeedMerge; for (size_t i=0;iP0(EdgeI[i]); CoordType P1=PolyF[i]->P1(EdgeI[i]); std::pair key(std::min(P0,P1),std::max(P0,P1)); NeedMerge.insert(key); } //then cycle and collapse do{ for (size_t i=0;i key(std::min(P0,P1),std::max(P0,P1)); if (NeedMerge.count(key)==0)continue; //do the merge MergeAlongEdge(poly_m,poly_m.face[i],j); //remove it NeedMerge.erase(key); break; } } vcg::tri::Allocator::CompactEveryVector(poly_m); }while (!NeedMerge.empty()); } static void Triangulate(PolyMeshType &poly_m, bool alsoTriangles = true, bool OnlyS=false) { size_t size0 = poly_m.face.size(); if (alsoTriangles) { for (size_t i=0; i 3) { Triangulate(poly_m, i); } } } } }; }//end namespace vcg #endif