vcglib/vcg/complex/algorithms/parametrization/poisson_solver.h

749 lines
24 KiB
C++

/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* 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 VCG_POISSON_SOLVER
#define VCG_POISSON_SOLVER
#include <eigenlib/Eigen/Sparse>
#include <vcg/complex/algorithms/clean.h>
#include <vcg/complex/algorithms/parametrization/distortion.h>
#include <vcg/complex/algorithms/parametrization/uv_utils.h>
namespace vcg {
namespace tri{
template <class MeshType>
class PoissonSolver
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType:: template PerFaceAttributeHandle<CoordType> PerFaceCoordHandle;
///the mesh itself
MeshType &mesh;
///solver data
std::map<VertexType*,int> VertexToInd;
std::map<int, VertexType*> IndToVertex;
///vertices to fix
std::vector<VertexType *> to_fix;
///unknown vector
Eigen::SparseMatrix<double> A; // A
Eigen::VectorXd b,x;// x and b
//number of variables
unsigned int n_vert_vars;
///total system size
unsigned int total_size;
///number of fixed variables
unsigned int n_fixed_vars;
///if you intend to follow the cross field
bool use_direction_field,fix_selected,correct_fixed;
///size of the scalar field
ScalarType fieldScale;
///handle per direction field
PerFaceCoordHandle Fh0,Fh1;
int VertexIndex(VertexType* v)
{
typename std::map<VertexType*,int>::iterator iteMap=VertexToInd.find(v);
assert(iteMap!=VertexToInd.end());
return ((*iteMap).second);
}
VertexType* IndexVertex(int index)
{
typename std::map<int,VertexType*>::iterator iteMap=IndToVertex.find(index);
assert(iteMap!=IndToVertex.end());
return ((*iteMap).second);
}
void AddVertexIndex(VertexType* v,int index)
{
VertexToInd.insert(std::pair<VertexType*,int>(v,index));
IndToVertex.insert(std::pair<int,VertexType*>(index,v));
}
///set the value of A of the system Ax=b
void SetValA(int Xindex,int Yindex,ScalarType val)
{
//int size=(int)S.nrows();
assert(0 <= Xindex && Xindex < int(total_size));
assert(0 <= Yindex && Yindex < int(total_size));
//S.A().addEntryReal(Xindex,Yindex,val);
//if (Xindex>=Yindex)
A.coeffRef(Xindex,Yindex) +=val;
}
void FindFarthestVert(VertexType* &v0,VertexType* &v1)
{
UpdateBounding<MeshType>::Box(mesh);
tri::UpdateTopology<MeshType>::FaceFace(mesh);
tri::UpdateFlags<MeshType>::FaceBorderFromFF(mesh);
tri::UpdateFlags<MeshType>::VertexBorderFromFace(mesh);
ScalarType dmax=0;
v0=NULL;
v1=NULL;
for (unsigned int i=0;i<mesh.vert.size();i++)
for (unsigned int j=(i+1);j<mesh.vert.size();j++)
{
VertexType *vt0=&mesh.vert[i];
VertexType *vt1=&mesh.vert[j];
if (vt0->IsD())continue;
if (vt1->IsD())continue;
if (!vt0->IsB())continue;
if (!vt1->IsB())continue;
ScalarType d_test=(vt0->P()-vt1->P()).Norm();
// ScalarType Dx=fabs(vt0->P().X()-vt1->P().X());
// ScalarType Dy=fabs(vt0->P().Y()-vt1->P().Y());
// ScalarType Dz=fabs(vt0->P().Z()-vt1->P().Z());
//ScalarType d_test=std::max(Dx,std::max(Dy,Dz));
//ScalarType d_test=std::max(fabs(Dx-Dy),std::max(fabs(Dx-Dz),fabs(Dy-Dz)));
if (d_test>dmax)
{
dmax=d_test;
v0=vt0;
v1=vt1;
}
}
assert(v0!=NULL);
assert(v1!=NULL);
}
///set the value of b of the system Ax=b
void SetValB(int Xindex,
ScalarType val)
{
/*S.b()[Xindex] += val;*/
b[Xindex] += val;
}
///add the area term, scalefactor is used to sum up
///and normalize on the overlap zones
void AddAreaTerm(int index[3][3][2],ScalarType ScaleFactor)
{
const ScalarType entry=0.5*ScaleFactor;
ScalarType val[3][3]= { {0, entry, -entry},
{-entry, 0, entry},
{entry, -entry, 0} };
for (int i=0;i<3;i++)
for (int j=0;j<3;j++)
{
///add for both u and v
int Xindex=index[i][j][0]*2;
int Yindex=index[i][j][1]*2;
SetValA(Xindex+1,Yindex,-val[i][j]);
SetValA(Xindex,Yindex+1,val[i][j]);
}
}
///set the diagonal of the matrix (which is zero at the beginning)
///as the sum of the other element inverted by sign
void SetDiagonal(ScalarType val[3][3])
{
for (int i=0;i<3;i++)
{
ScalarType sum=0;
for (int j=0;j<3;j++)
sum+=val[i][j];
val[i][i]=-sum;
}
}
///add this values to the right hand side
void AddRHS(ScalarType b[6],
int index[3])
{
for (int i=0;i<3;i++)
{
ScalarType valU=b[i*2];
ScalarType valV=b[(i*2)+1];
SetValB((index[i]*2),valU);
SetValB((index[i]*2)+1,valV);
}
}
///add a 3x3 block matrix to the system matrix...
///indexes are specified in the 3x3 matrix of x,y pairs
///indexes must be multiplied by 2 cause u and v
void Add33Block(ScalarType val[3][3],int index[3][3][2])
{
for (int i=0;i<3;i++)
for (int j=0;j<3;j++)
{
///add for both u and v
int Xindex=index[i][j][0]*2;
int Yindex=index[i][j][1]*2;
assert(Xindex<int(n_vert_vars*2));
assert(Yindex<int(n_vert_vars*2));
SetValA(Xindex,Yindex,val[i][j]);
SetValA(Xindex+1,Yindex+1,val[i][j]);
}
}
///add a 3x3 block matrix to the system matrix...
///indexes are specified in the 3x3 matrix of x,y pairs
///indexes must be multiplied by 2 cause u and v
void Add44Block(ScalarType val[4][4],int index[4][4][2])
{
for (int i=0;i<4;i++)
for (int j=0;j<4;j++)
{
///add for both u and v
int Xindex=index[i][j][0]*2;
int Yindex=index[i][j][1]*2;
assert(Xindex<(n_vert_vars*2));
assert(Yindex<(n_vert_vars*2));
SetValA(Xindex,Yindex,val[i][j]);
SetValA(Xindex+1,Yindex+1,val[i][j]);
}
}
///return the LHS for a given face
void perElementLHS(FaceType *f,
ScalarType val[3][3],
int index[3][3][2])
{
///initialize to zero
for (int x=0;x<3;x++)
for (int y=0;y<3;y++)
val[x][y]=0;
///get the vertices
VertexType *v[3];
v[0]=f->V(0);
v[1]=f->V(1);
v[2]=f->V(2);
///get the indexes of vertex instance (to consider cuts)
///for the current face
int Vindexes[3];
Vindexes[0]=VertexIndex(f->V(0));
Vindexes[1]=VertexIndex(f->V(1));
Vindexes[2]=VertexIndex(f->V(2));
///initialize the indexes for the block
for (int x=0;x<3;x++)
for (int y=0;y<3;y++)
{
index[x][y][0]=Vindexes[x];
index[x][y][1]=Vindexes[y];
}
///initialize edges
CoordType e[3];
for (int k=0;k<3;k++)
e[k]=v[(k+2)%3]->P()-v[(k+1)%3]->P();
///then consider area but also considering scale factor dur to overlaps
ScalarType areaT=((f->P(1)-f->P(0))^(f->P(2)-f->P(0))).Norm()/2.0;
for (int x=0;x<3;x++)
for (int y=0;y<3;y++)
if (x!=y)
{
ScalarType num=(e[x]*e[y]);
val[x][y] =num/(4.0*areaT);
}
///set the matrix as diagonal
SetDiagonal(val);
}
///return the RHS for a given face
void perElementRHS(FaceType *f,
ScalarType b[6],
ScalarType vector_field_scale=1)
{
/// then set the rhs
CoordType scaled_Kreal;
CoordType scaled_Kimag;
CoordType fNorm=f->N();
fNorm.Normalize();
CoordType p[3];
p[0]=f->P0(0);
p[1]=f->P0(1);
p[2]=f->P0(2);
CoordType neg_t[3];
neg_t[0] = fNorm ^ (p[2] - p[1]);
neg_t[1] = fNorm ^ (p[0] - p[2]);
neg_t[2] = fNorm ^ (p[1] - p[0]);
CoordType K1,K2;
/*MyMesh::PerFaceCoordHandle<ScalarType> Fh = tri::Allocator<MyMesh>::AddPerVertexAttribute<float> (m,std::string("Irradiance"));
bool CrossDir0 = tri::HasPerVertexAttribute(mesh,"CrossDir0");
bool CrossDir1 = tri::HasPerVertexAttribute(mesh,"CrossDir1");
assert(CrossDir0);
assert(CrossDir1);*/
//K1=f->Q3();
K1=Fh0[f];
K1.Normalize();
//K2=fNorm^K1;
K2=Fh1[f];
K2.Normalize();
scaled_Kreal = K1*(vector_field_scale);///2);
scaled_Kimag = K2*(vector_field_scale);///2);
b[0] = scaled_Kreal * neg_t[0];
b[1] = scaled_Kimag * neg_t[0];
b[2] = scaled_Kreal * neg_t[1];
b[3] = scaled_Kimag * neg_t[1];
b[4] = scaled_Kreal * neg_t[2];
b[5] = scaled_Kimag * neg_t[2];
////fine codice mio
}
///return the LHS and RHS for a given face
void PerElementSystemReal(FaceType *f,
ScalarType val[3][3],
int index[3][3][2],
ScalarType b[6],
ScalarType vector_field_scale=1.0)
{
perElementLHS(f,val,index);
if (use_direction_field)
perElementRHS(f,b,vector_field_scale);
}
void FixPointLSquares()
{
ScalarType penalization=1000;
int offset_row=n_vert_vars;
assert(to_fix.size()>0);
for (size_t i=0;i<to_fix.size();i++)
{
///take a vertex
VertexType *v=to_fix[i];
assert(!v->IsD());
int index=VertexIndex(v);
//v->vertex_index[0];
int indexvert=index*2;
int indexRow=(offset_row+i)*2;
SetValA(indexRow,indexRow,penalization);
SetValA(indexRow+1,indexRow+1,penalization);
///add values to the B vector
ScalarType U=v->T().U()*penalization;
ScalarType V=v->T().V()*penalization;
SetValB(indexRow,U);
SetValB(indexRow+1,V);
/*///set upper right part
SetValA(indexvert,indexCol,penalization);
SetValA(indexvert+1,indexCol+1,penalization);*/
SetValA(indexvert,indexvert,penalization);
SetValA(indexvert+1,indexvert+1,penalization);
SetValA(indexRow,indexRow,penalization);
SetValA(indexRow+1,indexRow+1,penalization);
SetValA(indexvert,indexRow,-penalization);
SetValA(indexvert+1,indexRow+1,-penalization);
SetValA(indexRow,indexvert,-penalization);
SetValA(indexRow+1,indexvert+1,-penalization);
//SetValA(indexCol+1,indexCol+1,-1);
}
}
//build the laplacian matrix cyclyng over all rangemaps
//and over all faces
void BuildLaplacianMatrix(double vfscale=1)
{
///then for each face
for (unsigned int j=0;j<mesh.face.size();j++)
{
FaceType *f=&mesh.face[j];
if (f->IsD())
continue;
int var_idx[3];//vertex variable indices
for(int k = 0; k < 3; ++k)
{
VertexType *v=f->V(k);
var_idx[k] = VertexIndex(v);
}
ScalarType val[3][3];
int index[3][3][2];
ScalarType b[6];
PerElementSystemReal(f, val,index, b, vfscale);
//Add the element to the matrix
Add33Block(val,index);
/////add area term.. to test if needed
/*if (!use_direction_field)
AddAreaTerm(index,1.0);//f->area);*/
/*ScalarType area=((f->P(1)-f->P(0))^(f->P(2)-f->P(0))).Norm();
if (!use_direction_field)
AddAreaTerm(index,area);*/
//ScalarType area=((f->P(1)-f->P(0))^(f->P(2)-f->P(0))).Norm();
if (!use_direction_field)
AddAreaTerm(index,1);
///add right hand side
if (use_direction_field)
AddRHS(b,var_idx);
}
}
void FindSizes()
{
// tag vertices and compute numbers of equations to determine the number of rows in the matrix
//TagVertices_Constrained(n_vert_vars, n_transition_eqs, n_align_sharp_eqs);
n_vert_vars=mesh.vn;
///initialize matrix size
total_size = (n_fixed_vars + n_vert_vars)*2;///must be multiplied by 2 becasue of u and v
}
void AllocateSystem()
{
//--- Allocates the data for Ax=b
A=Eigen::SparseMatrix<double>(total_size, total_size); // A
b = Eigen::VectorXd::Zero(total_size); // x and b
}
///intitialize the whole matrix
void InitMatrix()
{
FindSizes();
AllocateSystem();
}
bool Solve()
{
//return true;
A.finalize();
Eigen::SparseMatrix<double> As=Eigen::SparseMatrix<double>(A);
As.finalize();
Eigen::SimplicialCholesky<Eigen::SparseMatrix<double> > solver(As);
x = solver.solve(b);
return (solver.info()==Eigen::Success);
}
void InitIndex()
{
for (size_t i=0;i<mesh.vert.size();i++)
if (!mesh.vert[i].IsD())
AddVertexIndex(&mesh.vert[i],i);
}
///map back values to vertex
///if normalize==true then set the
///coordinates between 0 and 1
void MapCoords(bool normalize=false,
ScalarType /*fieldScale*/=1.0)
{
///clear Visited Flag
if (correct_fixed)
tri::UpdateFlags<MeshType>::VertexClearV(mesh);
//set fixed to V
for (size_t i=0;i<to_fix.size();i++)
to_fix[i]->SetV();
Box2<ScalarType> bbox;
if (normalize)
{
for (size_t i=0;i<n_vert_vars;i++)
{
ScalarType U=x[i*2];
ScalarType V=x[(i*2)+1];
bbox.Add(Point2<ScalarType>(U,V));
}
}
//for each vertex
for (size_t i=0;i<n_vert_vars;i++)
{
VertexType* v=IndexVertex(i);
//take U and V
ScalarType U=x[i*2];
ScalarType V=x[(i*2)+1];
Point2<ScalarType> p;
if (!v->IsV())
p=Point2<ScalarType>(U,V);
else
p=v->T().P();
//p/=fieldScale;
if (normalize)
{
p-=bbox.min;
p*=1/bbox.Diag();
}
v->T().P()=p;
}
///then copy to faces
for (size_t i=0;i<mesh.face.size();i++)
{
FaceType *f=&mesh.face[i];
for (int j=0;j<3;j++)
{
VertexType* v=f->V(j);
Point2<ScalarType> p=v->T().P();
f->WT(j).P()=p;
}
}
}
public:
///return true if is possible to
bool IsFeaseable()
{
tri::UpdateTopology<MeshType>::FaceFace(mesh);
int NNmanifoldE=tri::Clean<MeshType>::CountNonManifoldEdgeFF(mesh);
if (NNmanifoldE!=0)
{
printf("Non Manifold");
return false;
}
/*int NNmanifoldV=tri::Clean<MeshType>::CountNonManifoldVertexFF(mesh);
if (NNmanifoldV!=0)return false;*/
int G=tri::Clean<MeshType>::MeshGenus(mesh);
int numholes=tri::Clean<MeshType>::CountHoles(mesh);
if (numholes==0)
{
printf("Non omeomorph to a disc");
return false;
}
return (G==0);
}
///set the border as fixed
void SetBorderAsFixed()
{
for (size_t i=0;i<mesh.vert.size();i++)
{
VertexType* v=&mesh.vert[i];
if (v->IsD())continue;
if(v->IsB())to_fix.push_back(v);
}
std::sort(to_fix.begin(),to_fix.end());
typename std::vector<VertexType*>::iterator new_end=std::unique(to_fix.begin(),to_fix.end());
int dist=distance(to_fix.begin(),new_end);
to_fix.resize(dist);
}
///set selected vertices as fixed
void SetSelectedAsFixed()
{
for (int i=0;i<mesh.vert.size();i++)
{
VertexType* v=&mesh.vert[i];
if (v->IsD())continue;
if(v->IsS())to_fix.push_back(v);
}
std::sort(to_fix.begin(),to_fix.end());
typename std::vector<VertexType*>::iterator new_end=std::unique(to_fix.begin(),to_fix.end());
int dist=distance(to_fix.begin(),new_end);
to_fix.resize(dist);
}
///fix default vertices no need if already border on other vertices are fixed
///you need at least 2 fixed for solving without field ,
///while only 1 if you conforms to a given cross field
void FixDefaultVertices()
{
///in this case there are already vertices fixed, so no need to fix by default
assert(to_fix.size()==0);
///then fix only one vertex
if (use_direction_field)
{
for (size_t i=0;i<mesh.vert.size();i++)
if (!mesh.vert[i].IsD())
{
mesh.vert[i].T().P()=Point2<ScalarType>(0,0);
to_fix.push_back(&mesh.vert[i]);
return;
}
}
///then fix 2 vertices
else
{
VertexType *v0;
VertexType *v1;
FindFarthestVert(v0,v1);
if (v0==v1)
{
// tri::io::ExporterPLY<MeshType>::Save(mesh,"./parametrized.ply");
assert(0);
}
v0->T().P()=Point2<ScalarType>(0,0);
v1->T().P()=Point2<ScalarType>(1,0);
to_fix.push_back(v0);
to_fix.push_back(v1);
return;
}
}
///intialize parameters and setup fixed vertices vector
void Init(bool _use_direction_field=false,
bool _correct_fixed=true,
ScalarType _fieldScale=1.0)
{
use_direction_field=_use_direction_field;
//query if an attribute is present or not
if (use_direction_field)
{
bool CrossDir0 = tri::HasPerFaceAttribute(mesh,"CrossDir0");
bool CrossDir1 = tri::HasPerFaceAttribute(mesh,"CrossDir1");
assert(CrossDir0);
assert(CrossDir1);
Fh0= tri::Allocator<MeshType> :: template GetPerFaceAttribute<CoordType>(mesh,std::string("CrossDir0"));
Fh1= tri::Allocator<MeshType> :: template GetPerFaceAttribute<CoordType>(mesh,std::string("CrossDir1"));
}
correct_fixed=_correct_fixed;
fieldScale=_fieldScale;
to_fix.clear();
}
///solve the system, it return false if the matrix is singular
bool SolvePoisson(bool _write_messages=false,
ScalarType fieldScale=1.0,
bool solve_global_fold=true)
{
int t0,t1,t2,t3;
///Initializing Matrix
if (_write_messages)
{
printf("\n INITIALIZING THE MATRIX \n");
t0=clock();
}
///set vertex indexes
InitIndex();
/*///find vertex to fix
std::vector<VertexType *> to_fix;
FindFixedVertices(to_fix);
n_fixed_vars=to_fix.size();*/
if (use_direction_field)
{
assert(to_fix.size()>0);
}
else
{
assert(to_fix.size()>1);
}
n_fixed_vars=to_fix.size();
///initialize the matrix ALLOCATING SPACE
InitMatrix();
if (use_direction_field)
{
bool CrossDir0 = tri::HasPerFaceAttribute(mesh,"CrossDir0");
bool CrossDir1 = tri::HasPerFaceAttribute(mesh,"CrossDir1");
assert(CrossDir0);
assert(CrossDir1);
}
///build the laplacian system
BuildLaplacianMatrix(fieldScale);
////add the lagrange multiplier
FixPointLSquares();
if (_write_messages)
{
t1=clock();
printf("\n time:%d \n",t1-t0);
printf("\n SOLVING \n");
}
//int n_vars=(n_vert_vars)*2;
//int integer_constr_size=(n_transition_vars+n_fixed_vars+n_bary_transition_vars)*2;
//X=std::vector< double >(n_vars+n_fixed_vars*2);
bool done=Solve();
if (!done)
return false;
if (_write_messages)
{
t2=clock();
printf("\n time:%d \n",t2-t1);
printf("\n ASSIGNING COORDS \n");
}
MapCoords(false,fieldScale);
if (_write_messages)
{
t3=clock();
printf("\n time:%d \n",t3-t2);
}
///then check if majority of faces are folded
if (!solve_global_fold) return true;
if (tri::Distortion<MeshType,false>::GloballyUnFolded(mesh))
{
tri::UV_Utils<MeshType>::GloballyMirrorX(mesh);
bool isUnfolded = tri::Distortion<MeshType,false>::GloballyUnFolded(mesh);
assert( ! isUnfolded);
}
return true;
}
PoissonSolver(MeshType &_mesh):mesh(_mesh)
{
assert(mesh.vert.size()>3);
assert(mesh.face.size()>1);
}
}; // end class
} //End Namespace Tri
} // End Namespace vcg
#endif