1017 lines
36 KiB
C++
1017 lines
36 KiB
C++
/****************************************************************************
|
|
* 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/algorithms/update/normal.h>
|
|
#include <vcg/complex/complex.h>
|
|
#include <vcg/space/polygon3.h>
|
|
#include <vcg/complex/algorithms/update/color.h>
|
|
#include <vcg/complex/algorithms/closest.h>
|
|
#include <vcg/complex/algorithms/point_sampling.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::CoordType CoordType;
|
|
typedef typename PolyMeshType::ScalarType ScalarType;
|
|
typedef typename vcg::face::Pos<FaceType> PosType;
|
|
public:
|
|
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,
|
|
ScalarType angleDeg=100)
|
|
{
|
|
//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 (size_t 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++)
|
|
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 (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();
|
|
}
|
|
|
|
public:
|
|
|
|
|
|
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++)
|
|
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++)
|
|
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 Origin(0,0,0);
|
|
for (int j=0;j<f.VN();j++)
|
|
AVN+=f.V(j)->N();
|
|
|
|
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]-=Origin;
|
|
TemplatePos[j]=Rot*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)
|
|
{
|
|
(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();
|
|
|
|
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++)
|
|
{
|
|
std::vector<typename PolygonType::CoordType> 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;//poly_m.face[i].Q();
|
|
//ScalarType W=1;
|
|
|
|
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=avgPos[i]/weightSum[i];
|
|
newP=newP*(1-alpha)+AvVert[i]*alpha;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*! \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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*! \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)
|
|
{
|
|
//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);
|
|
}
|
|
|
|
/*! \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,
|
|
bool fixIrr=false,
|
|
int nstep=100,
|
|
ScalarType Damp=0.5)
|
|
{
|
|
typedef typename TriMeshType::FaceType FaceType;
|
|
typedef vcg::GridStaticPtr<FaceType, ScalarType> TriMeshGrid;
|
|
TriMeshGrid grid;
|
|
|
|
//initialize the grid
|
|
grid.Set(tri_mesh.face.begin(),tri_mesh.face.end());
|
|
|
|
ScalarType MaxD=tri_mesh.bbox.Diag();
|
|
|
|
if (fixIrr)
|
|
poly_m.NumIrregular(true);
|
|
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 (fixIrr && (poly_m.vert[i].IsS()))continue;
|
|
poly_m.vert[i].P()=poly_m.vert[i].P()*Damp+
|
|
AvVert[i]*(1-Damp);
|
|
}
|
|
|
|
|
|
for (size_t i=0;i<poly_m.vert.size();i++)
|
|
{
|
|
CoordType testPos=poly_m.vert[i].P();
|
|
CoordType closestPt;
|
|
ScalarType minDist;
|
|
FaceType *f=NULL;
|
|
CoordType norm,ip;
|
|
f=vcg::tri::GetClosestFaceBase(tri_mesh,grid,testPos,MaxD,minDist,closestPt,norm,ip);
|
|
poly_m.vert[i].P()=poly_m.vert[i].P()*Damp+
|
|
closestPt*(1-Damp);
|
|
poly_m.vert[i].N()=norm;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*! \brief This function laplacian smooth of a polygonal mesh
|
|
*/
|
|
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 fixIrr=false,
|
|
ScalarType Damp=0.5)
|
|
{
|
|
vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(poly_m);
|
|
|
|
//UpdateBorderVertexFromPFFAdj(poly_m);
|
|
vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(poly_m);
|
|
|
|
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,fixIrr);
|
|
//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;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*! \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 fixIrr=false,
|
|
ScalarType Damp=0.5)
|
|
{
|
|
//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);
|
|
|
|
//optimize it
|
|
vcg::PolygonalAlgorithm<PolyMeshType>::SmoothReprojectPCA<TempMesh>(poly_m,GuideSurf,relaxStep,fixIrr,Damp);
|
|
}
|
|
|
|
/*! \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 (size_t 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 RemoveValence2BorderVertices(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 (size_t 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
|
|
if ((!v0->IsB())||(!v1->IsB())||(!v2->IsB()))continue;
|
|
CoordType dir0=(v0->P()-v1->P());
|
|
CoordType dir1=(v2->P()-v1->P());
|
|
dir0.Normalize();
|
|
dir1.Normalize();
|
|
ScalarType testDot=(dir0*dir1);
|
|
if (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)]++;
|
|
|
|
//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 (size_t 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;
|
|
FaceV.push_back(v);
|
|
}
|
|
|
|
//then deallocate face
|
|
if (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 void CollapseBorderSmallEdges(PolyMeshType &poly_m,const ScalarType perc_average=0.3)
|
|
{
|
|
//compute the average edge
|
|
ScalarType AvEdge=AverageEdge(poly_m);
|
|
ScalarType minLimit=AvEdge*perc_average;
|
|
|
|
while(CollapseBorderSmallEdgesStep(poly_m,minLimit)){};
|
|
|
|
RemoveValence2Faces(poly_m);
|
|
|
|
RemoveValence2BorderVertices(poly_m);
|
|
}
|
|
|
|
/*! \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]);
|
|
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].VN();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void Triangulate(PolyMeshType &poly_m,size_t IndexF)
|
|
{
|
|
|
|
CoordType bary=vcg::PolyBarycenter(poly_m.face[IndexF]);
|
|
size_t sizeV=poly_m.face[IndexF].VN();
|
|
|
|
//add the new vertex
|
|
vcg::tri::Allocator<PolyMeshType>::AddVertex(poly_m,bary);
|
|
VertexType *newV=&poly_m.vert.back();
|
|
|
|
std::vector<size_t> ToUpdateF;
|
|
|
|
//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;
|
|
ToUpdateF.push_back(poly_m.face.size()-1);
|
|
}
|
|
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;
|
|
ToUpdateF.push_back(IndexF);
|
|
}
|
|
|
|
|
|
static void Triangulate(PolyMeshType &poly_m)
|
|
{
|
|
size_t size0=poly_m.face.size();
|
|
for (size_t i=0;i<size0;i++)
|
|
Triangulate(poly_m,i);
|
|
}
|
|
|
|
template <class TriMeshType>
|
|
static void TriangulateToTriMesh(PolyMeshType &poly_m,TriMeshType &triangle_mesh)
|
|
{
|
|
triangle_mesh.Clear();
|
|
|
|
PolyMeshType PolySwap;
|
|
vcg::tri::Append<PolyMeshType,PolyMeshType>::Mesh(PolySwap,poly_m);
|
|
Triangulate(PolySwap);
|
|
|
|
//then copy onto the triangle mesh
|
|
vcg::tri::Append<TriMeshType,PolyMeshType>::Mesh(triangle_mesh,PolySwap);
|
|
}
|
|
};
|
|
|
|
}//end namespace vcg
|
|
|
|
#endif
|