You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1582 lines
57 KiB
1582 lines
57 KiB
/**************************************************************************** |
|
* 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 <vcg/complex/complex.h> |
|
#include <vcg/complex/algorithms/update/normal.h> |
|
#include <vcg/space/polygon3.h> |
|
#include <vcg/complex/algorithms/update/color.h> |
|
#include <vcg/complex/algorithms/closest.h> |
|
#include <vcg/complex/algorithms/update/quality.h> |
|
#include <wrap/io_trimesh/export_obj.h> |
|
|
|
//define a temporary triangle mesh type |
|
class TempFace; |
|
class TempVertex; |
|
|
|
struct TempUsedTypes: public vcg::UsedTypes<vcg::Use<TempVertex>::AsVertexType, |
|
vcg::Use<TempFace>::AsFaceType>{}; |
|
|
|
class TempVertex:public vcg::Vertex<TempUsedTypes, |
|
vcg::vertex::Coord3d, |
|
vcg::vertex::Normal3d, |
|
vcg::vertex::BitFlags> |
|
{}; |
|
|
|
class TempFace:public vcg::Face<TempUsedTypes, |
|
vcg::face::VertexRef, |
|
vcg::face::BitFlags, |
|
vcg::face::FFAdj, |
|
vcg::face::Mark, |
|
vcg::face::Normal3d> |
|
{}; |
|
|
|
|
|
class TempMesh: public vcg::tri::TriMesh< std::vector<TempVertex>,std::vector<TempFace > > |
|
{}; |
|
|
|
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 PolyMeshType> |
|
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<FaceType> PosType; |
|
|
|
static void SetFacePos(PolyMeshType &poly_m, |
|
int IndexF,std::vector<CoordType> &Pos) |
|
{ |
|
poly_m.face[IndexF].Dealloc(); |
|
poly_m.face[IndexF].Alloc(Pos.size()); |
|
//std::cout<<Pos.size()<<std::endl; |
|
int sizeV=poly_m.vert.size(); |
|
for (size_t i=0;i<Pos.size();i++) |
|
vcg::tri::Allocator<PolyMeshType>::AddVertex(poly_m,Pos[i]); |
|
|
|
for (size_t i=0;i<Pos.size();i++) |
|
poly_m.face[IndexF].V(i)=&poly_m.vert[sizeV+i]; |
|
} |
|
|
|
public: |
|
|
|
static void SubdivideStep(PolyMeshType &poly_m) |
|
{ |
|
//get the barycenters |
|
std::vector<CoordType> Bary; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
CoordType bary(0,0,0); |
|
for (size_t j=0;j<poly_m.face[i].VN();j++) |
|
bary+=poly_m.face[i].P(j); |
|
|
|
bary/=poly_m.face[i].VN(); |
|
Bary.push_back(bary); |
|
} |
|
|
|
//get center of edge |
|
std::map<std::pair<CoordType,CoordType>, CoordType> EdgeVert; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
for (size_t j=0;j<poly_m.face[i].VN();j++) |
|
{ |
|
CoordType Pos0=poly_m.face[i].P0(j); |
|
CoordType Pos1=poly_m.face[i].P1(j); |
|
CoordType Avg=(Pos0+Pos1)/2; |
|
std::pair<CoordType,CoordType> Key(std::min(Pos0,Pos1),std::max(Pos0,Pos1)); |
|
EdgeVert[Key]=Avg; |
|
} |
|
|
|
int sizeF=poly_m.face.size(); |
|
for (size_t i=0;i<sizeF;i++) |
|
{ |
|
//retrieve the sequence of pos |
|
std::vector<CoordType> Pos; |
|
for (size_t j=0;j<poly_m.face[i].VN();j++) |
|
{ |
|
CoordType Pos0=poly_m.face[i].P0(j); |
|
CoordType Pos1=poly_m.face[i].P1(j); |
|
std::pair<CoordType,CoordType> 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<CoordType> 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<sizeV;j+=2) |
|
{ |
|
vcg::tri::Allocator<PolyMeshType>::AddFaces(poly_m,1); |
|
std::vector<CoordType> 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<PolyMeshType>::RemoveDuplicateVertex(poly_m); |
|
vcg::tri::Allocator<PolyMeshType>::CompactEveryVector(poly_m); |
|
} |
|
|
|
static bool CollapseEdges(PolyMeshType &poly_m, |
|
const std::vector<PosType> &CollapsePos, |
|
const std::vector<CoordType> &InterpPos) |
|
{ |
|
|
|
//this set how to remap the vertices after deletion |
|
std::map<VertexType*,VertexType*> VertexRemap; |
|
|
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexClearS(poly_m); |
|
|
|
bool collapsed=false; |
|
//go over all faces and check the ones needed to be deleted |
|
for (size_t i=0;i<CollapsePos.size();i++) |
|
{ |
|
FaceType *currF=CollapsePos[i].F(); |
|
int IndexE=CollapsePos[i].E(); |
|
size_t NumV=currF->VN(); |
|
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<poly_m.face.size();i++) |
|
{ |
|
int NumV=poly_m.face[i].VN(); |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
//get the two vertices of the edge |
|
VertexType *v0=poly_m.face[i].V(j); |
|
//see if it must substituted or not |
|
if (VertexRemap.count(v0)==0)continue; |
|
//in that case remap to the new one |
|
VertexType *newV=VertexRemap[v0]; |
|
//assign new vertex |
|
poly_m.face[i].V(j)=newV; |
|
} |
|
} |
|
|
|
//then re-elaborate the face |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
//get vertices of the face |
|
int NumV=poly_m.face[i].VN(); |
|
std::vector<VertexType*> FaceV; |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
VertexType *v0=poly_m.face[i].V(j); |
|
VertexType *v1=poly_m.face[i].V((j+1)%NumV); |
|
if(v0==v1)continue; |
|
FaceV.push_back(v0); |
|
} |
|
|
|
//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<FaceV.size();j++) |
|
poly_m.face[i].V(j)=FaceV[j]; |
|
} |
|
|
|
//remove unreferenced vertices |
|
vcg::tri::Clean<PolyMeshType>::RemoveUnreferencedVertex(poly_m); |
|
|
|
//and compact them |
|
vcg::tri::Allocator<PolyMeshType>::CompactEveryVector(poly_m); |
|
|
|
return collapsed; |
|
} |
|
private: |
|
|
|
|
|
static bool CollapseBorderSmallEdgesStep(PolyMeshType &poly_m, |
|
const ScalarType edge_limit) |
|
{ |
|
//update topology |
|
vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(poly_m); |
|
|
|
//update border vertices |
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m); |
|
|
|
|
|
vcg::tri::UpdateSelection<PolyMeshType>::VertexCornerBorder(poly_m,math::ToRad(150.0)); |
|
|
|
std::vector<PosType> CollapsePos; |
|
std::vector<CoordType> InterpPos; |
|
|
|
//go over all faces and check the ones needed to be deleted |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
int NumV=poly_m.face[i].VN(); |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
VertexType *v0=poly_m.face[i].V(j); |
|
VertexType *v1=poly_m.face[i].V((j+1)%NumV); |
|
assert(v0!=v1); |
|
|
|
bool IsBV0=v0->IsB(); |
|
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<CoordType> &AvVert) |
|
{ |
|
//cumulate step |
|
AvVert.clear(); |
|
AvVert.resize(poly_m.vert.size(),CoordType(0,0,0)); |
|
std::vector<ScalarType> AvSum(poly_m.vert.size(),0); |
|
for (size_t i=0;i<poly_m.face.size();i++) { |
|
if (poly_m.face[i].IsD()) |
|
continue; |
|
|
|
for (size_t j=0;j<(size_t)poly_m.face[i].VN();j++) |
|
{ |
|
//get current vertex |
|
VertexType *currV=poly_m.face[i].V(j); |
|
//and its position |
|
CoordType currP=currV->P(); |
|
//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<poly_m.vert.size();i++) |
|
{ |
|
if (poly_m.vert[i].IsD()) |
|
continue; |
|
|
|
if (AvSum[i]==0)continue; |
|
AvVert[i]/=AvSum[i]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
static void UpdateNormal(FaceType &F) |
|
{ |
|
F.N()=vcg::PolygonNormal(F); |
|
} |
|
|
|
static void UpdateNormalByFitting(FaceType &F) |
|
{ |
|
UpdateNormal(F); |
|
vcg::Plane3<ScalarType> PlF; |
|
PlF=PolyFittingPlane(F); |
|
if ((PlF.Direction()*F.N())<0) |
|
F.N()=-PlF.Direction(); |
|
else |
|
F.N()=PlF.Direction(); |
|
} |
|
|
|
static void DisplaceBySelected(FaceType &f,std::vector<CoordType> &TemplatePos, |
|
bool FixS,bool FixB) |
|
{ |
|
CoordType AvPosF(0,0,0); |
|
CoordType AvPosT(0,0,0); |
|
size_t Num=0; |
|
for (size_t i=0;i<f.VN();i++) |
|
{ |
|
bool AddVal=false; |
|
AddVal|=((FixS)&&(f.V(i)->IsS())); |
|
AddVal|=((FixB)&&(f.V(i)->IsB())); |
|
if (!AddVal)continue; |
|
Num++; |
|
AvPosF+=f.V(i)->P(); |
|
AvPosT+=TemplatePos[i]; |
|
} |
|
if (Num==0)return; |
|
AvPosF/=(ScalarType)Num; |
|
AvPosT/=(ScalarType)Num; |
|
CoordType Displ=AvPosF-AvPosT; |
|
for (size_t i=0;i<TemplatePos.size();i++) |
|
TemplatePos[i]+=Displ; |
|
} |
|
|
|
public: |
|
|
|
static void SelectIrregularInternal(PolyMeshType &poly_m) |
|
{ |
|
vcg::tri::UpdateQuality<PolyMeshType>::VertexValence(poly_m); |
|
vcg::tri::UpdateSelection<PolyMeshType>::VertexClear(poly_m); |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if (poly_m.vert[i].IsB())continue; |
|
if (poly_m.vert[i].Q()==4)continue; |
|
poly_m.vert[i].SetS(); |
|
} |
|
} |
|
|
|
static void SelectIrregularBorder(PolyMeshType &poly_m) |
|
{ |
|
vcg::tri::UpdateQuality<PolyMeshType>::VertexValence(poly_m); |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if (!poly_m.vert[i].IsB())continue; |
|
if (poly_m.vert[i].Q()==2)continue; |
|
poly_m.vert[i].SetS(); |
|
} |
|
} |
|
|
|
static CoordType GetFaceGetBary(FaceType &F) |
|
{ |
|
CoordType bary=PolyBarycenter(F); |
|
return bary; |
|
} |
|
|
|
/*! \brief update the face normal by averaging among vertex's |
|
* normals computed between adjacent edges |
|
*/ |
|
static void UpdateFaceNormals(PolyMeshType &poly_m) |
|
{ |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
if (!poly_m.face[i].IsD()) |
|
UpdateNormal(poly_m.face[i]); |
|
} |
|
|
|
/*! \brief update the face normal by fitting a plane |
|
*/ |
|
static void UpdateFaceNormalByFitting(PolyMeshType &poly_m) |
|
{ |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
if (!poly_m.face[i].IsD()) |
|
UpdateNormalByFitting(poly_m.face[i]); |
|
} |
|
|
|
|
|
enum PolyQualityType{QAngle,QPlanar,QTemplate}; |
|
|
|
/*! \brief update the quality of the faces by considering different possibilities |
|
* QAngle = consider the angle deviation from ideal one (ex 90° quad, 60° triangle...) |
|
* QPlanar = consider the difference wrt interpolating plane |
|
* QTemplate= consider the difference wrt template polygon as in "Statics Aware Grid Shells" |
|
*/ |
|
static void UpdateQuality(PolyMeshType &poly_m, |
|
const PolyQualityType &QType) |
|
{ |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].IsD())continue; |
|
switch (QType) |
|
{ |
|
case QAngle: |
|
ScalarType AvgDev,WorstDev; |
|
vcg::PolyAngleDeviation(poly_m.face[i],AvgDev,WorstDev); |
|
poly_m.face[i].Q()=WorstDev; |
|
break; |
|
case QPlanar: |
|
poly_m.face[i].Q()=vcg::PolyFlatness(poly_m.face[i]); |
|
break; |
|
default: |
|
poly_m.face[i].Q()=vcg::PolyAspectRatio(poly_m.face[i],true); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/*! \brief given a face this function returns the template positions as in "Statics Aware Grid Shells" |
|
*/ |
|
static void GetRotatedTemplatePos(FaceType &f, |
|
std::vector<CoordType> &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;j<f.VN();j++) |
|
// AVN0=AVN0+f.V(j)->N(); |
|
|
|
CoordType AVN=vcg::PolygonNormal(f); |
|
//AVN0.Normalize(); |
|
// std::cout<<"AVN "<<AVN.X()<<","<<AVN.Y()<<","<<AVN.Z()<<std::endl; |
|
// std::cout<<"AVN0 "<<AVN0.X()<<","<<AVN0.Y()<<","<<AVN0.Z()<<std::endl; |
|
// std::cout<<"NormT "<<NormT.X()<<","<<NormT.Y()<<","<<NormT.Z()<<std::endl; |
|
|
|
for (size_t j=0;j<TemplatePos.size();j++) |
|
Origin+=TemplatePos[j]; |
|
|
|
Origin/=(ScalarType)TemplatePos.size(); |
|
AVN.Normalize(); |
|
|
|
//find rotation matrix |
|
vcg::Matrix33<ScalarType> Rot=vcg::RotationMatrix(NormT,AVN); |
|
|
|
//apply transformation |
|
for (size_t j=0;j<TemplatePos.size();j++) |
|
{ |
|
TemplatePos[j]=TemplatePos[j]-Origin; |
|
TemplatePos[j]=Rot*TemplatePos[j]; |
|
TemplatePos[j]=TemplatePos[j]+Origin; |
|
} |
|
} |
|
|
|
/*! \brief This function performs the polygon regularization as in "Statics Aware Grid Shells" |
|
*/ |
|
static void SmoothPCA(PolyMeshType &poly_m, |
|
int relax_step=10, |
|
ScalarType Damp=0.5, |
|
bool FixS=false, |
|
bool isotropic=true, |
|
ScalarType smoothTerm=0.1, |
|
bool fixB=true, |
|
bool WeightByQuality=false, |
|
const std::vector<bool> *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<poly_m.face.size();i++) |
|
MeshArea+=vcg::PolyArea(poly_m.face[i]); |
|
|
|
ScalarType AvgArea=MeshArea/(ScalarType)poly_m.face.size(); |
|
|
|
if (WeightByQuality) |
|
UpdateQuality(poly_m,QTemplate); |
|
|
|
if (IgnoreF!=NULL){assert((*IgnoreF).size()==poly_m.face.size());} |
|
for (size_t s=0;s<(size_t)relax_step;s++) |
|
{ |
|
//initialize the accumulation vector |
|
std::vector<CoordType> avgPos(poly_m.vert.size(),CoordType(0,0,0)); |
|
std::vector<ScalarType> weightSum(poly_m.vert.size(),0); |
|
//then compute the templated positions |
|
|
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if ((IgnoreF!=NULL)&&((*IgnoreF)[i]))continue; |
|
std::vector<typename PolygonType::CoordType> TemplatePos; |
|
GetRotatedTemplatePos(poly_m.face[i],TemplatePos); |
|
if ((FixS)||(fixB)) |
|
DisplaceBySelected(poly_m.face[i],TemplatePos,FixS,fixB); |
|
|
|
//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<TemplatePos.size();j++) |
|
{ |
|
int IndexV=vcg::tri::Index(poly_m,poly_m.face[i].V(j)); |
|
CoordType Pos=TemplatePos[j]; |
|
//sum up contributes |
|
avgPos[IndexV]+=Pos*W; |
|
weightSum[IndexV]+=W; |
|
} |
|
|
|
} |
|
|
|
//get the laplacian contribute |
|
std::vector<CoordType> AvVert; |
|
LaplacianPos(poly_m,AvVert); |
|
//then update the position |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
ScalarType alpha=smoothTerm;//PolyNormDeviation(poly_m.face[i]); |
|
// if (alpha<0)alpha=0; |
|
// if (alpha>1)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 (std::isnan(newP.X())|| std::isnan(newP.Y())|| std::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 "<<weightSum[i]<<std::endl; |
|
newP=newP*(1-alpha)+AvVert[i]*alpha; |
|
//newP=AvVert[i]; |
|
if ((fixB)&&(poly_m.vert[i].IsB()))continue; |
|
if ((FixS)&&(poly_m.vert[i].IsS()))continue; |
|
poly_m.vert[i].P()=poly_m.vert[i].P()*Damp+ |
|
newP*(1-Damp); |
|
} |
|
} |
|
} |
|
|
|
template <class TriMeshType> |
|
static void ReprojectBorder(PolyMeshType &poly_m, |
|
TriMeshType &tri_mesh, |
|
bool FixS=true) |
|
{ |
|
//then reproject on border |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if (!poly_m.vert[i].IsB())continue; |
|
if (FixS && poly_m.vert[i].IsS())continue; |
|
|
|
CoordType testPos=poly_m.vert[i].P(); |
|
ScalarType minD=std::numeric_limits<ScalarType>::max(); |
|
CoordType closPos; |
|
for (size_t j=0;j<tri_mesh.face.size();j++) |
|
for (size_t k=0;k<3;k++) |
|
{ |
|
//check if border edge |
|
if (tri_mesh.face[j].FFp(k)!=(&tri_mesh.face[j]))continue; |
|
|
|
CoordType P0,P1; |
|
P0.Import(tri_mesh.face[j].cP0(k)); |
|
P1.Import(tri_mesh.face[j].cP1(k)); |
|
vcg::Segment3<ScalarType> 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 <class TriMeshType> |
|
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<PolyMeshType>::VertexClearS(poly_m); |
|
|
|
//update topology |
|
vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(poly_m); |
|
|
|
//update border vertices |
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m); |
|
|
|
//select corner vertices on the border |
|
ScalarType angleRad=angleDeg * M_PI / 180; |
|
vcg::tri::UpdateSelection<PolyMeshType>::VertexCornerBorder(poly_m,angleRad); |
|
|
|
for (int s=0;s<nstep;s++) |
|
{ |
|
std::vector<CoordType> AvVert; |
|
LaplacianPos(poly_m,AvVert); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if (!poly_m.vert[i].IsB())continue; |
|
if (poly_m.vert[i].IsS())continue; |
|
poly_m.vert[i].P()=poly_m.vert[i].P()*Damp+ |
|
AvVert[i]*(1-Damp); |
|
} |
|
|
|
// //then reproject on border |
|
// for (size_t i=0;i<poly_m.vert.size();i++) |
|
// { |
|
// if (!poly_m.vert[i].IsB())continue; |
|
// if (poly_m.vert[i].IsS())continue; |
|
|
|
// CoordType testPos=poly_m.vert[i].P(); |
|
// ScalarType minD=std::numeric_limits<ScalarType>::max(); |
|
// CoordType closPos; |
|
// for (size_t j=0;j<tri_mesh.face.size();j++) |
|
// for (size_t k=0;k<3;k++) |
|
// { |
|
// if (tri_mesh.face[j].FFp(k)!=(&tri_mesh.face[j]))continue; |
|
|
|
// CoordType P0,P1; |
|
// P0.Import(tri_mesh.face[j].cP0(k)); |
|
// P1.Import(tri_mesh.face[j].cP1(k)); |
|
// vcg::Segment3<ScalarType> 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<TempMesh,PolyMeshType>::ImportFromPolyMesh(GuideSurf,poly_m); |
|
vcg::tri::UpdateBounding<TempMesh>::Box(GuideSurf); |
|
vcg::tri::UpdateNormal<TempMesh>::PerVertexNormalizedPerFace(GuideSurf); |
|
vcg::tri::UpdateTopology<TempMesh>::FaceFace(GuideSurf); |
|
vcg::tri::UpdateFlags<TempMesh>::FaceBorderFromFF(GuideSurf); |
|
|
|
LaplacianReprojectBorder<TempMesh>(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 <class TriMeshType> |
|
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<TriFaceType, TriScalarType> TriMeshGrid; |
|
TriMeshGrid grid; |
|
tri::MeshAssert<TriMeshType>::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<nstep;s++) |
|
{ |
|
std::vector<CoordType> AvVert; |
|
LaplacianPos(poly_m,AvVert); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if (poly_m.vert[i].IsB()) continue; |
|
if(poly_m.vert[i].IsD() || (OnlyOnSelected && !poly_m.vert[i].IsS())) continue; |
|
poly_m.vert[i].P()=poly_m.vert[i].P()*DampS+ |
|
AvVert[i]*(1-DampS); |
|
} |
|
|
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if(poly_m.vert[i].IsD() || (OnlyOnSelected && !poly_m.vert[i].IsS())) continue; |
|
TriCoordType testPos; |
|
testPos.Import(poly_m.vert[i].P()); |
|
TriCoordType closestPt; |
|
TriScalarType minDist; |
|
TriFaceType *f=NULL; |
|
TriCoordType norm,ip; |
|
f=vcg::tri::GetClosestFaceBase(tri_mesh,grid,testPos,MaxD,minDist,closestPt,norm,ip); |
|
CoordType closestImp; |
|
closestImp.Import(closestPt); |
|
poly_m.vert[i].P()=poly_m.vert[i].P()*DampR+ |
|
closestImp*(1-DampR); |
|
CoordType normalImp; |
|
normalImp.Import(norm); |
|
poly_m.vert[i].N()=normalImp; |
|
} |
|
} |
|
|
|
} |
|
|
|
static void LaplacianReproject(PolyMeshType &poly_m, |
|
int nstep=100, |
|
ScalarType Damp=0.5, |
|
bool OnlyOnSelected=false) |
|
{ |
|
//transform into triangular |
|
TempMesh GuideSurf; |
|
//vcg::tri::PolygonSupport<TempMesh,PolyMeshType>:(GuideSurf,poly_m); |
|
TriangulateToTriMesh<TempMesh>(poly_m,GuideSurf); |
|
vcg::tri::UpdateBounding<TempMesh>::Box(GuideSurf); |
|
vcg::tri::UpdateNormal<TempMesh>::PerVertexNormalizedPerFace(GuideSurf); |
|
vcg::tri::UpdateTopology<TempMesh>::FaceFace(GuideSurf); |
|
vcg::tri::UpdateFlags<TempMesh>::FaceBorderFromFF(GuideSurf); |
|
LaplacianReproject<TempMesh>(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<nstep;s++) |
|
{ |
|
std::vector<CoordType> AvVert; |
|
LaplacianPos(poly_m,AvVert); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
if ((FixS) && (poly_m.vert[i].IsS()))continue; |
|
poly_m.vert[i].P()=poly_m.vert[i].P()*Damp+ |
|
AvVert[i]*(1-Damp); |
|
} |
|
} |
|
|
|
} |
|
|
|
/*! \brief This function performs the polygon regularization as in "Statics Aware Grid Shells" |
|
* followed by a reprojection step on the triangle mesh passed as parameter |
|
*/ |
|
template <class TriMeshType> |
|
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<PolyMeshType>::VertexClearS(poly_m); |
|
|
|
vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(poly_m); |
|
|
|
//UpdateBorderVertexFromPFFAdj(poly_m); |
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m); |
|
|
|
std::vector<std::vector<vcg::Line3<ScalarType> > > 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<ScalarType> 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;i<poly_m.vert.size();i++) |
|
{ |
|
if (SharpEdge[i].size()==0)continue; |
|
if (SharpEdge[i].size()>2)poly_m.vert[i].SetS(); |
|
} |
|
} |
|
// if (fixIrr) |
|
// { |
|
// vcg::tri::UpdateQuality<PolyMeshType>::VertexValence(poly_m); |
|
// for (size_t i=0;i<poly_m.vert.size();i++) |
|
// { |
|
// if (poly_m.vert[i].IsB())continue; |
|
// if (poly_m.vert[i].Q()==4)continue; |
|
// poly_m.vert[i].SetS(); |
|
// } |
|
// } |
|
|
|
|
|
typedef typename TriMeshType::FaceType FaceType; |
|
typedef vcg::GridStaticPtr<FaceType, typename TriMeshType::ScalarType> 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;i<poly_m.face.size();i++) |
|
// poly_m.face[i].Q()=vcg::PolyArea(poly_m.face[i]); |
|
|
|
// for (size_t i=0;i<poly_m.vert.size();i++) |
|
// { |
|
// typename TriMeshType::CoordType testPos; |
|
// testPos.Import(poly_m.vert[i].P()); |
|
// typename TriMeshType::CoordType closestPt; |
|
// typename TriMeshType::ScalarType minDist; |
|
// typename TriMeshType::FaceType *f=NULL; |
|
// typename TriMeshType::CoordType norm,ip; |
|
// f=vcg::tri::GetClosestFaceBase(tri_mesh,grid,testPos,MaxD,minDist,closestPt,norm,ip); |
|
// //poly_m.vert[i].N().Import(norm); |
|
// } |
|
|
|
for(int k=0;k<relaxStep;k++) |
|
{ |
|
//smooth PCA step |
|
SmoothPCA(poly_m,1,Damp,fixS,true,0.1,FixB,WeightByQuality); |
|
//reprojection step |
|
//laplacian smooth step |
|
//Laplacian(poly_m,Damp,1); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
typename TriMeshType::CoordType testPos; |
|
testPos.Import(poly_m.vert[i].P()); |
|
typename TriMeshType::CoordType closestPt; |
|
typename TriMeshType::ScalarType minDist; |
|
if ((FixB)&&(poly_m.vert[i].IsB())) |
|
{continue;} |
|
else |
|
if (SharpEdge[i].size()==0)//reproject onto original mesh |
|
{ |
|
FaceType *f=NULL; |
|
typename TriMeshType::CoordType norm,ip; |
|
f=vcg::tri::GetClosestFaceBase(tri_mesh,grid,testPos,MaxD,minDist,closestPt,norm,ip); |
|
poly_m.vert[i].P().Import(testPos*Damp+closestPt*(1-Damp)); |
|
//poly_m.vert[i].N().Import(norm); |
|
} |
|
else //reproject onto segments |
|
{ |
|
CoordType av_closest(0,0,0); |
|
size_t sum=0; |
|
for (size_t j=0;j<SharpEdge[i].size();j++) |
|
{ |
|
CoordType currPos; |
|
currPos.Import(testPos); |
|
CoordType closest; |
|
ScalarType dist; |
|
vcg::LinePointDistance(SharpEdge[i][j],currPos,closest,dist); |
|
av_closest+=closest; |
|
sum++; |
|
} |
|
assert(sum>0); |
|
poly_m.vert[i].P()=av_closest/sum; |
|
} |
|
} |
|
if (!FixB) |
|
ReprojectBorder(poly_m,tri_mesh,true); |
|
UpdateFaceNormals(poly_m); |
|
vcg::tri::UpdateNormal<PolyMeshType>::PerVertexFromCurrentFaceNormal(poly_m); |
|
} |
|
|
|
} |
|
|
|
|
|
template <class TriMeshType> |
|
static void TriangulateToTriMesh(PolyMeshType &poly_m,TriMeshType &triangle_mesh, bool alsoTriangles = true) |
|
{ |
|
triangle_mesh.Clear(); |
|
|
|
PolyMeshType PolySwap; |
|
vcg::tri::Append<PolyMeshType,PolyMeshType>::Mesh(PolySwap,poly_m); |
|
Triangulate(PolySwap, alsoTriangles); |
|
|
|
//then copy onto the triangle mesh |
|
vcg::tri::Append<TriMeshType,PolyMeshType>::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<TempMesh,PolyMeshType>:(GuideSurf,poly_m); |
|
TriangulateToTriMesh<TempMesh>(poly_m,GuideSurf); |
|
vcg::tri::UpdateBounding<TempMesh>::Box(GuideSurf); |
|
vcg::tri::UpdateNormal<TempMesh>::PerVertexNormalizedPerFace(GuideSurf); |
|
vcg::tri::UpdateTopology<TempMesh>::FaceFace(GuideSurf); |
|
vcg::tri::UpdateFlags<TempMesh>::FaceBorderFromFF(GuideSurf); |
|
|
|
//optimize it |
|
vcg::PolygonalAlgorithm<PolyMeshType>::SmoothReprojectPCA<TempMesh>(poly_m,GuideSurf,relaxStep,fixS,Damp,SharpDeg,WeightByQuality,FixB); |
|
} |
|
|
|
static void Reproject(PolyMeshType &poly_m, |
|
PolyMeshType &target) |
|
{ |
|
vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(poly_m); |
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m); |
|
|
|
//transform into triangular |
|
TempMesh GuideSurf; |
|
//vcg::tri::PolygonSupport<TempMesh,PolyMeshType>:(GuideSurf,poly_m); |
|
TriangulateToTriMesh<TempMesh>(target,GuideSurf); |
|
vcg::tri::UpdateBounding<TempMesh>::Box(GuideSurf); |
|
vcg::tri::UpdateNormal<TempMesh>::PerVertexNormalizedPerFace(GuideSurf); |
|
vcg::tri::UpdateTopology<TempMesh>::FaceFace(GuideSurf); |
|
vcg::tri::UpdateFlags<TempMesh>::FaceBorderFromFF(GuideSurf); |
|
|
|
//initialize the grid |
|
typedef typename TempMesh::FaceType FaceType; |
|
typedef vcg::GridStaticPtr<FaceType, typename TempMesh::ScalarType> TriMeshGrid; |
|
TriMeshGrid grid; |
|
grid.Set(GuideSurf.face.begin(),GuideSurf.face.end()); |
|
|
|
ScalarType MaxD=GuideSurf.bbox.Diag(); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
//reproject on border later |
|
if (poly_m.vert[i].IsB())continue; |
|
typename TempMesh::CoordType testPos; |
|
testPos.Import(poly_m.vert[i].P()); |
|
typename TempMesh::CoordType closestPt; |
|
typename TempMesh::ScalarType minDist; |
|
typename TempMesh::FaceType *f=NULL; |
|
typename TempMesh::CoordType norm,ip; |
|
f=vcg::tri::GetClosestFaceBase(GuideSurf,grid,testPos,MaxD,minDist,closestPt,norm,ip); |
|
poly_m.vert[i].P()=closestPt; |
|
} |
|
//then reprojec the border |
|
ReprojectBorder(poly_m,GuideSurf); |
|
} |
|
|
|
template <class TriMesh> |
|
static void ReprojectonTriMesh(PolyMeshType &poly_m, |
|
TriMesh &target) |
|
{ |
|
vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(poly_m); |
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m); |
|
|
|
|
|
//initialize the grid |
|
typedef typename TriMesh::FaceType FaceType; |
|
typedef vcg::GridStaticPtr<FaceType, typename TriMesh::ScalarType> TriMeshGrid; |
|
TriMeshGrid grid; |
|
grid.Set(target.face.begin(),target.face.end()); |
|
|
|
ScalarType MaxD=target.bbox.Diag(); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
//reproject on border later |
|
if (poly_m.vert[i].IsB())continue; |
|
typename TriMesh::CoordType testPos; |
|
testPos.Import(poly_m.vert[i].P()); |
|
typename TriMesh::CoordType closestPt; |
|
typename TriMesh::ScalarType minDist; |
|
typename TriMesh::FaceType *f=NULL; |
|
typename TriMesh::CoordType norm,ip; |
|
f=vcg::tri::GetClosestFaceBase(target,grid,testPos,MaxD,minDist,closestPt,norm,ip); |
|
poly_m.vert[i].P()=closestPt; |
|
} |
|
//then reprojec the border |
|
ReprojectBorder(poly_m,target); |
|
} |
|
|
|
/*! \brief This function return average edge size |
|
*/ |
|
static ScalarType AverageEdge(const PolyMeshType &poly_m) |
|
{ |
|
ScalarType AvL=0; |
|
size_t numE=0; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
int NumV=poly_m.face[i].VN(); |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
CoordType pos0=poly_m.face[i].cV(j)->P(); |
|
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<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].VN()>=3)continue; |
|
vcg::tri::Allocator<PolyMeshType>::DeleteFace(poly_m,poly_m.face[i]); |
|
} |
|
|
|
//then remove unreferenced vertices |
|
vcg::tri::Clean<PolyMeshType>::RemoveUnreferencedVertex(poly_m); |
|
vcg::tri::Allocator<PolyMeshType>::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<PolyMeshType>::FaceFace(poly_m); |
|
|
|
//update border vertices |
|
//UpdateBorderVertexFromPFFAdj(poly_m); |
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m); |
|
|
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexClearS(poly_m); |
|
|
|
//select corners |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].IsD())continue; |
|
|
|
//get vertices of the face |
|
int NumV=poly_m.face[i].VN(); |
|
|
|
for (int j=0;j<NumV;j++) |
|
{ |
|
VertexType *v0=poly_m.face[i].V((j+NumV-1)%NumV); |
|
VertexType *v1=poly_m.face[i].V(j); |
|
VertexType *v2=poly_m.face[i].V((j+1)%NumV); |
|
//must be 3 borders |
|
bool IsB=((v0->IsB())&&(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<size_t> valenceVertH = |
|
vcg::tri::Allocator<PolyMeshType>:: template GetPerVertexAttribute<size_t> (poly_m); |
|
|
|
//initialize to zero |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
valenceVertH[i]=0; |
|
|
|
//then sum up the valence |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
for (int j=0;j<poly_m.face[i].VN();j++) |
|
valenceVertH[poly_m.face[i].V(j)]++; |
|
|
|
//cannot collapse triangular vertices otherwise will collapse to a segment |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].VN()>3)continue; |
|
for (int j=0;j<poly_m.face[i].VN();j++) |
|
valenceVertH[poly_m.face[i].V(j)]=3; |
|
} |
|
|
|
//then re-elaborate the faces |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].IsD())continue; |
|
|
|
//get vertices of the face |
|
int NumV=poly_m.face[i].VN(); |
|
|
|
std::vector<VertexType*> FaceV; |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
VertexType *v=poly_m.face[i].V(j); |
|
assert(!v->IsD()); |
|
//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<FaceV.size();j++) |
|
poly_m.face[i].V(j)=FaceV[j]; |
|
} |
|
|
|
//then remove unreferenced vertices |
|
vcg::tri::Clean<PolyMeshType>::RemoveUnreferencedVertex(poly_m); |
|
vcg::tri::Allocator<PolyMeshType>::CompactEveryVector(poly_m); |
|
|
|
vcg::tri::Allocator<PolyMeshType>::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<steps;s++) |
|
{ |
|
std::vector<std::vector<CoordType> > VertPos(poly_m.vert.size()); |
|
|
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].IsD())continue; |
|
|
|
if (OnlySFaces && (!poly_m.face[i].IsS()))continue; |
|
//get vertices of the face |
|
int NumV=poly_m.face[i].VN(); |
|
if (NumV<=3)continue; |
|
|
|
//save vertice's positions |
|
std::vector<CoordType> FacePos; |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
VertexType *v=poly_m.face[i].V(j); |
|
assert(!v->IsD()); |
|
FacePos.push_back(v->P()); |
|
} |
|
|
|
//then fit the plane |
|
vcg::Plane3<ScalarType> FitPl; |
|
vcg::FitPlaneToPointSet(FacePos,FitPl); |
|
|
|
//project each point onto fitting plane |
|
for (int j=0;j<NumV;j++) |
|
{ |
|
VertexType *v=poly_m.face[i].V(j); |
|
int IndexV=vcg::tri::Index(poly_m,v); |
|
CoordType ProjP=FitPl.Projection(v->P()); |
|
VertPos[IndexV].push_back(ProjP); |
|
} |
|
} |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
{ |
|
CoordType AvgPos(0,0,0); |
|
|
|
for (size_t j=0;j<VertPos[i].size();j++) |
|
AvgPos+=VertPos[i][j]; |
|
|
|
if (VertPos[i].size()==0)continue; |
|
|
|
AvgPos/=(ScalarType)VertPos[i].size(); |
|
|
|
MaxDispl=std::max(MaxDispl,(poly_m.vert[i].P()-AvgPos).Norm()); |
|
poly_m.vert[i].P()=AvgPos; |
|
} |
|
} |
|
return MaxDispl; |
|
} |
|
|
|
static ScalarType Area(PolyMeshType &poly_m) |
|
{ |
|
ScalarType MeshArea=0; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
MeshArea+=vcg::PolyArea(poly_m.face[i]); |
|
return MeshArea; |
|
} |
|
|
|
static void InitQualityVertVoronoiArea(PolyMeshType &poly_m) |
|
{ |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
poly_m.vert[i].Q()=0; |
|
|
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
// ScalarType AreaF=vcg::PolyArea(poly_m.face[i]); |
|
size_t sizeV=poly_m.face[i].VN()-1; |
|
CoordType baryF=vcg::PolyBarycenter(poly_m.face[i]); |
|
for (int j=0;j<poly_m.face[i].VN();j++) |
|
{ |
|
CoordType P0=poly_m.face[i].P((j+sizeV-1)%sizeV); |
|
CoordType P1=poly_m.face[i].P(j); |
|
CoordType P2=poly_m.face[i].P1(j); |
|
vcg::Triangle3<ScalarType> T0(P1,(P0+P1)/2,baryF); |
|
vcg::Triangle3<ScalarType> 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<PolyMeshType>::PerVertexFromCurrentFaceNormal(poly_m); |
|
ScalarType MaxA=0; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
poly_m.face[i].Q()=PolygonTorsion(poly_m.face[i]); |
|
MaxA=std::max(MaxA,poly_m.face[i].Q()); |
|
} |
|
return MaxA; |
|
} |
|
|
|
static ScalarType InitQualityFaceBending(PolyMeshType &poly_m) |
|
{ |
|
UpdateFaceNormalByFitting(poly_m); |
|
vcg::tri::UpdateNormal<PolyMeshType>::PerVertexFromCurrentFaceNormal(poly_m); |
|
ScalarType MaxA=0; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
poly_m.face[i].Q()=PolygonBending(poly_m.face[i]); |
|
MaxA=std::max(MaxA,poly_m.face[i].Q()); |
|
} |
|
return MaxA; |
|
} |
|
|
|
|
|
static void InitQualityVertEdgeLenght(PolyMeshType &poly_m) |
|
{ |
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
poly_m.vert[i].Q()=0; |
|
|
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
for (int j=0;j<poly_m.face[i].VN();j++) |
|
{ |
|
FaceType *f=&poly_m.face[i]; |
|
FaceType *f1=f->FFp(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<ScalarType> SumW(poly_m.vert.size(),0); |
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++) |
|
poly_m.vert[i].Q()=0; |
|
|
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
ScalarType AreaF=vcg::PolyArea(poly_m.face[i]); |
|
for (size_t j=0;j<poly_m.face[i].VN();j++) |
|
{ |
|
poly_m.face[i].V(j)->Q()+=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;i<poly_m.vert.size();i++) |
|
{ |
|
if (SumW[i]>0) |
|
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<ScalarType>::max(); |
|
CloseF=-1; |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
CoordType closeTest; |
|
ScalarType currD=vcg::PolygonPointDistance(poly_m.face[i],pos,closeTest); |
|
if (currD>minD)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<PolyMeshType>::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<PolyMeshType>::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<sizeN); |
|
std::vector<VertexType*> NewV; |
|
for (size_t i=0;i<sizeN;i++) |
|
{ |
|
int IndexV=(i+StartI)%sizeN; |
|
NewV.push_back(f.V(IndexV)); |
|
} |
|
//then reset all vertices |
|
for (size_t i=0;i<sizeN;i++) |
|
f.V(i)=NewV[i]; |
|
} |
|
|
|
static void MergeAlongEdge(PolyMeshType &poly_m, |
|
FaceType &f, |
|
const size_t &EdgeI) |
|
{ |
|
//cannot be a border |
|
assert(f.FFp(EdgeI)!=&f); |
|
FaceType *f1=f.FFp(EdgeI); |
|
int EdgeI1=f.FFi(EdgeI); |
|
|
|
//sort first face |
|
int FirstV0=(EdgeI+1) % f.VN(); |
|
ReorderFaceVert(f,FirstV0); |
|
int FirstV1=(EdgeI1+1)%f1->VN(); |
|
ReorderFaceVert(*f1,FirstV1); |
|
|
|
std::vector<VertexType*> 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<NewV.size();i++) |
|
f.V(i)=NewV[i]; |
|
|
|
vcg::tri::Allocator<PolyMeshType>::DeleteFace(poly_m,*f1); |
|
} |
|
|
|
static void MergeAlongEdges(PolyMeshType &poly_m, |
|
const std::vector<FaceType*> &PolyF, |
|
const std::vector<size_t> &EdgeI) |
|
{ |
|
//create a table with all edges that have to be merged |
|
std::set<std::pair<CoordType,CoordType> > NeedMerge; |
|
for (size_t i=0;i<PolyF.size();i++) |
|
{ |
|
CoordType P0=PolyF[i]->P0(EdgeI[i]); |
|
CoordType P1=PolyF[i]->P1(EdgeI[i]); |
|
std::pair<CoordType,CoordType> key(std::min(P0,P1),std::max(P0,P1)); |
|
NeedMerge.insert(key); |
|
} |
|
|
|
//then cycle and collapse |
|
do{ |
|
for (size_t i=0;i<poly_m.face.size();i++) |
|
{ |
|
if (poly_m.face[i].IsD())continue; |
|
for (size_t j=0;j<poly_m.face[i].VN();j++) |
|
{ |
|
CoordType P0=poly_m.face[i].P0(j); |
|
CoordType P1=poly_m.face[i].P1(j); |
|
std::pair<CoordType,CoordType> 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<PolyMeshType>::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<size0; i++) |
|
{ |
|
if ((OnlyS)&&(!poly_m.face[i].IsS()))continue; |
|
Triangulate(poly_m, i); |
|
} |
|
} |
|
else |
|
{ |
|
for (size_t i=0; i<size0; i++) |
|
{ |
|
if ((OnlyS)&&(!poly_m.face[i].IsS()))continue; |
|
if (poly_m.face[i].VN() > 3) |
|
{ |
|
Triangulate(poly_m, i); |
|
} |
|
} |
|
} |
|
} |
|
|
|
}; |
|
|
|
}//end namespace vcg |
|
|
|
#endif
|
|
|