refactored laplacian smoothing and added taubin smoothing
This commit is contained in:
parent
3af17fab9a
commit
a8becdc8f1
|
@ -260,6 +260,8 @@ static void VertexCoordScaleDependentLaplacian_Fujiwara(MeshType &m, int step, S
|
||||||
class LaplacianInfo
|
class LaplacianInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
LaplacianInfo(const CoordType &_p, const int _n):sum(_p),cnt(_n) {}
|
||||||
|
LaplacianInfo() {}
|
||||||
CoordType sum;
|
CoordType sum;
|
||||||
ScalarType cnt;
|
ScalarType cnt;
|
||||||
};
|
};
|
||||||
|
@ -267,79 +269,163 @@ public:
|
||||||
// Classical Laplacian Smoothing. Each vertex can be moved onto the average of the adjacent vertices.
|
// Classical Laplacian Smoothing. Each vertex can be moved onto the average of the adjacent vertices.
|
||||||
// Can smooth only the selected vertices and weight the smoothing according to the quality
|
// Can smooth only the selected vertices and weight the smoothing according to the quality
|
||||||
// In the latter case 0 means that the vertex is not moved and 1 means that the vertex is moved onto the computed position.
|
// In the latter case 0 means that the vertex is not moved and 1 means that the vertex is moved onto the computed position.
|
||||||
|
//
|
||||||
|
// From the Taubin definition "A signal proc approach to fair surface design"
|
||||||
|
// We define the discrete Laplacian of a discrete surface signal by weighted averages over the neighborhoods
|
||||||
|
// \delta xi = \Sum wij (xj - xi) ;
|
||||||
|
// where xj are the adjacent vertices of xi and wij is usually 1/n_adj
|
||||||
|
//
|
||||||
|
// This function simply accumulate over a TempData all the positions of the ajacent vertices
|
||||||
|
//
|
||||||
|
static void AccumulateLaplacianInfo(MeshType &m, SimpleTempData<typename MeshType::VertContainer,LaplacianInfo > &TD)
|
||||||
|
{
|
||||||
|
FaceIterator fi;
|
||||||
|
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||||
|
{
|
||||||
|
if(!(*fi).IsD())
|
||||||
|
for(int j=0;j<3;++j)
|
||||||
|
if(!(*fi).IsB(j))
|
||||||
|
{
|
||||||
|
TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
|
||||||
|
TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
|
||||||
|
++TD[(*fi).V(j)].cnt;
|
||||||
|
++TD[(*fi).V1(j)].cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// si azzaera i dati per i vertici di bordo
|
||||||
|
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||||
|
{
|
||||||
|
if(!(*fi).IsD())
|
||||||
|
for(int j=0;j<3;++j)
|
||||||
|
if((*fi).IsB(j))
|
||||||
|
{
|
||||||
|
TD[(*fi).V0(j)].sum=(*fi).P0(j);
|
||||||
|
TD[(*fi).V1(j)].sum=(*fi).P1(j);
|
||||||
|
TD[(*fi).V0(j)].cnt=1;
|
||||||
|
TD[(*fi).V1(j)].cnt=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// se l'edge j e' di bordo si deve mediare solo con gli adiacenti
|
||||||
|
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||||
|
{
|
||||||
|
if(!(*fi).IsD())
|
||||||
|
for(int j=0;j<3;++j)
|
||||||
|
if((*fi).IsB(j))
|
||||||
|
{
|
||||||
|
TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
|
||||||
|
TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
|
||||||
|
++TD[(*fi).V(j)].cnt;
|
||||||
|
++TD[(*fi).V1(j)].cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void VertexCoordLaplacian(MeshType &m, int step, bool SmoothSelected=false, float QualityWeight=0)
|
static void VertexCoordLaplacian(MeshType &m, int step, bool SmoothSelected=false)
|
||||||
|
{
|
||||||
|
VertexIterator vi;
|
||||||
|
LaplacianInfo lpz(CoordType(0,0,0),0);
|
||||||
|
SimpleTempData<typename MeshType::VertContainer,LaplacianInfo > TD(m.vert,lpz);
|
||||||
|
for(int i=0;i<step;++i)
|
||||||
|
{
|
||||||
|
TD.Init(lpz);
|
||||||
|
AccumulateLaplacianInfo(m,TD);
|
||||||
|
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||||
|
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
||||||
|
{
|
||||||
|
if(!SmoothSelected || (*vi).IsS())
|
||||||
|
(*vi).P() = ( (*vi).P() + TD[*vi].sum)/(TD[*vi].cnt+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VertexCoordLaplacianBlend(MeshType &m, int step, float alpha, bool SmoothSelected=false)
|
||||||
|
{
|
||||||
|
VertexIterator vi;
|
||||||
|
LaplacianInfo lpz(CoordType(0,0,0),0);
|
||||||
|
assert (alpha<= 1.0);
|
||||||
|
SimpleTempData<typename MeshType::VertContainer,LaplacianInfo > TD(m.vert);
|
||||||
|
|
||||||
|
for(int i=0;i<step;++i)
|
||||||
|
{
|
||||||
|
TD.Init(lpz);
|
||||||
|
AccumulateLaplacianInfo(m,TD);
|
||||||
|
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||||
|
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
||||||
|
{
|
||||||
|
if(!SmoothSelected || (*vi).IsS())
|
||||||
|
{
|
||||||
|
CoordType Delta = TD[*vi].sum/TD[*vi].cnt - (*vi).P();
|
||||||
|
(*vi).P() = (*vi).P() + Delta*alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a couple of notes about the lambda mu values
|
||||||
|
We assume that 0 < lambda , and mu is a negative scale factor such that mu < - lambda.
|
||||||
|
Holds mu+lambda < 0 (e.g in absolute value mu is greater)
|
||||||
|
|
||||||
|
let kpb be the pass-band frequency, taubin says that:
|
||||||
|
kpb = 1/lambda + 1/mu >0
|
||||||
|
|
||||||
|
Values of kpb from 0.01 to 0.1 produce good results according to the original paper.
|
||||||
|
|
||||||
|
kpb * mu - mu/lambda = 1
|
||||||
|
mu = 1/(kpb-1/lambda )
|
||||||
|
|
||||||
|
So if lambda == 0.5 -> mu = 1/(0.1 - 2) = -0.53
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static void VertexCoordTaubin(MeshType &m, int step, float lambda, float mu, bool SmoothSelected=false)
|
||||||
|
{
|
||||||
|
LaplacianInfo lpz(CoordType(0,0,0),0);
|
||||||
|
SimpleTempData<typename MeshType::VertContainer,LaplacianInfo > TD(m.vert,lpz);
|
||||||
|
VertexIterator vi;
|
||||||
|
for(int i=0;i<step;++i)
|
||||||
|
{
|
||||||
|
TD.Init(lpz);
|
||||||
|
AccumulateLaplacianInfo(m,TD);
|
||||||
|
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||||
|
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
||||||
|
{
|
||||||
|
if(!SmoothSelected || (*vi).IsS())
|
||||||
|
{
|
||||||
|
CoordType Delta = TD[*vi].sum/TD[*vi].cnt - (*vi).P();
|
||||||
|
(*vi).P() = (*vi).P() + Delta*lambda ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||||
|
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
||||||
|
{
|
||||||
|
if(!SmoothSelected || (*vi).IsS())
|
||||||
|
{
|
||||||
|
CoordType Delta = TD[*vi].sum/TD[*vi].cnt - (*vi).P();
|
||||||
|
(*vi).P() = (*vi).P() - Delta*mu ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end for step
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void VertexCoordLaplacianQuality(MeshType &m, int step, bool SmoothSelected=false)
|
||||||
{
|
{
|
||||||
LaplacianInfo lpz;
|
LaplacianInfo lpz;
|
||||||
lpz.sum=CoordType(0,0,0);
|
lpz.sum=CoordType(0,0,0);
|
||||||
lpz.cnt=1;
|
lpz.cnt=1;
|
||||||
SimpleTempData<typename MeshType::VertContainer,LaplacianInfo > TD(m.vert,lpz);
|
SimpleTempData<typename MeshType::VertContainer,LaplacianInfo > TD(m.vert,lpz);
|
||||||
for(int i=0;i<step;++i)
|
for(int i=0;i<step;++i)
|
||||||
{
|
|
||||||
VertexIterator vi;
|
|
||||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
|
||||||
{
|
{
|
||||||
TD[*vi].cnt=1;
|
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||||
TD[*vi].sum=(*vi).P();
|
|
||||||
}
|
|
||||||
FaceIterator fi;
|
|
||||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
|
||||||
if(!(*fi).IsD())
|
|
||||||
for(int j=0;j<3;++j)
|
|
||||||
if(!(*fi).IsB(j))
|
|
||||||
{
|
|
||||||
TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
|
|
||||||
TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
|
|
||||||
++TD[(*fi).V(j)].cnt;
|
|
||||||
++TD[(*fi).V1(j)].cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// si azzaera i dati per i vertici di bordo
|
|
||||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
|
||||||
if(!(*fi).IsD())
|
|
||||||
for(int j=0;j<3;++j)
|
|
||||||
if((*fi).IsB(j))
|
|
||||||
{
|
|
||||||
//TD[(*fi).V(j)]=lpz;
|
|
||||||
//TD[(*fi).V1(j)]=lpz;
|
|
||||||
TD[(*fi).V0(j)].sum=(*fi).P0(j);
|
|
||||||
TD[(*fi).V1(j)].sum=(*fi).P1(j);
|
|
||||||
TD[(*fi).V0(j)].cnt=1;
|
|
||||||
TD[(*fi).V1(j)].cnt=1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// se l'edge j e' di bordo si deve mediare solo con gli adiacenti
|
|
||||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
|
||||||
if(!(*fi).IsD())
|
|
||||||
for(int j=0;j<3;++j)
|
|
||||||
if((*fi).IsB(j))
|
|
||||||
{
|
|
||||||
TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
|
|
||||||
TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
|
|
||||||
++TD[(*fi).V(j)].cnt;
|
|
||||||
++TD[(*fi).V1(j)].cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(QualityWeight>0)
|
|
||||||
{ // quality weighted smoothing
|
|
||||||
// We assume that weights are in the 0..1 range.
|
|
||||||
assert(tri::HasPerVertexQuality(m));
|
|
||||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
|
||||||
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
||||||
if(!SmoothSelected || (*vi).IsS())
|
if(!SmoothSelected || (*vi).IsS())
|
||||||
{
|
{
|
||||||
float q=(*vi).Q();
|
float q=(*vi).Q();
|
||||||
(*vi).P()=(*vi).P()*q + (TD[*vi].sum/TD[*vi].cnt)*(1.0-q);
|
(*vi).P()=(*vi).P()*q + (TD[*vi].sum/TD[*vi].cnt)*(1.0-q);
|
||||||
}
|
}
|
||||||
}
|
} // end for
|
||||||
else
|
|
||||||
{
|
|
||||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
|
||||||
if(!(*vi).IsD() && TD[*vi].cnt>0 )
|
|
||||||
if(!SmoothSelected || (*vi).IsS())
|
|
||||||
(*vi).P()=TD[*vi].sum/TD[*vi].cnt;
|
|
||||||
}
|
|
||||||
} // end for
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue