SetQasDistorsion now: (1) returns the total per-mesh distortion, properly aggregated (2) can compute L2 and L-inf energies

This commit is contained in:
mtarini 2016-04-13 11:46:38 +00:00
parent 480af9d03d
commit 5c37e87b06
1 changed files with 153 additions and 25 deletions

View File

@ -29,6 +29,27 @@
namespace vcg { namespace vcg {
namespace tri{ namespace tri{
template <class MeshType, bool PerWedgeFlag> template <class MeshType, bool PerWedgeFlag>
/*
* Energy types:
*
* AreaDist : 0 for equiareal (equipotent) mappings
* EdgeDist (hack): 0 for isometric mappings (computed on edges only)
* AngleDist (hack): 0 for conformal mappings
* CrossDist : as above, but computed on tangent directions (not UVs)
* L2Stretch : 1 for isometric mappings (averaged case on the mesh),
* +inf on degenerate / folded cases
* Described in [1]
* LInfStretch : as above, but WORST case
* (returns the worst stretch on any position and direction)
* Described in [1]
*
* [1] Sander, P. V., Snyder, J., Gortler, S. J., & Hoppe, H.
* "Texture mapping progressive meshes."
* In Proc. ACM SIGGRAPH (pp. 409-416). 2001
*/
class Distortion class Distortion
{ {
public: public:
@ -36,7 +57,9 @@ public:
typedef typename MeshType::VertexType VertexType; typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::CoordType CoordType; typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::FaceType::CurVecType CurVecType;
typedef typename MeshType::FaceType::TexCoordType::ScalarType TexScalarType; typedef typename MeshType::FaceType::TexCoordType::ScalarType TexScalarType;
typedef Point2<TexScalarType> TexCoordType;
static ScalarType Area3D(const FaceType *f) static ScalarType Area3D(const FaceType *f)
{ {
@ -45,7 +68,7 @@ public:
static ScalarType AreaUV(const FaceType *f) static ScalarType AreaUV(const FaceType *f)
{ {
Point2<TexScalarType> uv0,uv1,uv2; TexCoordType uv0,uv1,uv2;
if(PerWedgeFlag) { if(PerWedgeFlag) {
uv0=f->cWT(0).P(); uv0=f->cWT(0).P();
uv1=f->cWT(1).P(); uv1=f->cWT(1).P();
@ -152,7 +175,7 @@ public:
public: public:
enum DistType{AreaDist,EdgeDist,AngleDist,CrossDist}; enum DistType{AreaDist,EdgeDist,AngleDist,CrossDist,L2Stretch,LInfStretch};
///return the absolute difference between angle in 3D space and texture space ///return the absolute difference between angle in 3D space and texture space
///Actually the difference in cos space ///Actually the difference in cos space
@ -206,7 +229,7 @@ public:
} }
///return the variance of edge length, normalized in absolute value, ///return the variance of edge length, normalized in absolute value,
// the needed scaling factor EdgeScaleVal may be calculated ///the needed scaling factor EdgeScaleVal may be calculated
///by using the ScalingFactor function ///by using the ScalingFactor function
static ScalarType EdgeDistortion(const FaceType *f,int e, static ScalarType EdgeDistortion(const FaceType *f,int e,
ScalarType EdgeScaleVal) ScalarType EdgeScaleVal)
@ -233,6 +256,60 @@ public:
return diff; return diff;
} }
static ScalarType L2StretchEnergySquared(const FaceType *f,
ScalarType AreaScaleVal)
{
TexCoordType p0 = (PerWedgeFlag)? f->cWT(0).P() : f->cV(0)->T().P() ;
TexCoordType p1 = (PerWedgeFlag)? f->cWT(1).P() : f->cV(1)->T().P() ;
TexCoordType p2 = (PerWedgeFlag)? f->cWT(2).P() : f->cV(2)->T().P() ;
CoordType q0 = f->cP(0);
CoordType q1 = f->cP(1);
CoordType q2 = f->cP(2);
TexScalarType A2 = ((p1-p0)^(p2-p0));
if (A2<0) A2 = 0; // will be NAN, +infinity
CoordType Ss = ( q0 * ( p1[1]-p2[1] ) + q1 * (p2[1]-p0[1]) + q2 * (p0[1]-p1[1]) ) / A2;
CoordType St = ( q0 * ( p2[0]-p1[0] ) + q1 * (p0[0]-p2[0]) + q2 * (p1[0]-p0[0]) ) / A2;
ScalarType a = Ss.SquaredNorm() / AreaScaleVal;
ScalarType c = St.SquaredNorm() / AreaScaleVal;
return ((a+c)/2);
}
static ScalarType LInfStretchEnergy(const FaceType *f, ScalarType AreaScaleVal)
{
TexCoordType p0 = (PerWedgeFlag)? f->cWT(0).P() : f->cV(0)->T().P() ;
TexCoordType p1 = (PerWedgeFlag)? f->cWT(1).P() : f->cV(1)->T().P() ;
TexCoordType p2 = (PerWedgeFlag)? f->cWT(2).P() : f->cV(2)->T().P() ;
CoordType q0 = f->cP(0);
CoordType q1 = f->cP(1);
CoordType q2 = f->cP(2);
TexScalarType A2 = ((p1-p0)^(p2-p0));
if (A2<0) A2 = 0; // will be NAN, +infinity
CoordType Ss = ( q0 * ( p1[1]-p2[1] ) + q1 * (p2[1]-p0[1]) + q2 * (p0[1]-p1[1]) ) / A2;
CoordType St = ( q0 * ( p2[0]-p1[0] ) + q1 * (p0[0]-p2[0]) + q2 * (p1[0]-p0[0]) ) / A2;
ScalarType a = Ss.SquaredNorm() / AreaScaleVal;
ScalarType b = Ss*St / AreaScaleVal;
ScalarType c = St.SquaredNorm() / AreaScaleVal;
ScalarType delta = sqrt((a-c)*(a-c)+4*b*b);
ScalarType G = sqrt( (a+c+delta)/2 );
//ScalarType g = sqrt( (a+c-delta)/2 ); // not needed
return G;
}
///return the number of folded faces ///return the number of folded faces
static bool Folded(const FaceType *f) static bool Folded(const FaceType *f)
{ {
@ -271,10 +348,10 @@ public:
return UDdist; return UDdist;
} }
static void SetQasCrossDirDistortion(MeshType &m) static ScalarType SetFQAsCrossDirDistortion(MeshType &m)
{ {
//first save the old UV dir //first save the old UV dir
std::vector<CoordType> Dir1,Dir2; std::vector<CurVecType> Dir1,Dir2;
for (size_t i=0;i<m.face.size();i++) for (size_t i=0;i<m.face.size();i++)
{ {
Dir1.push_back(m.face[i].PD1()); Dir1.push_back(m.face[i].PD1());
@ -282,14 +359,18 @@ public:
} }
vcg::tri::CrossField<MeshType>::InitDirFromWEdgeUV(m); vcg::tri::CrossField<MeshType>::InitDirFromWEdgeUV(m);
ScalarType tot = 0, totA = 0;
//then compute angle deficit //then compute angle deficit
for (size_t i=0;i<m.face.size();i++) for (size_t i=0;i<m.face.size();i++)
{ {
CoordType transfPD1=vcg::tri::CrossField<MeshType>::K_PI(Dir1[i],
m.face[i].PD1(), FaceType &f( m.face[i] );
m.face[i].N()); CoordType transfPD1=vcg::tri::CrossField<MeshType>::K_PI(CoordType::Construct( Dir1[i] ),
CoordType::Construct( f.PD1() ),
f.N());
transfPD1.Normalize(); transfPD1.Normalize();
ScalarType AngleDeficit=vcg::Angle(transfPD1,m.face[i].PD1()); ScalarType AngleDeficit=vcg::Angle(transfPD1,CoordType::Construct( f.PD1() ));
AngleDeficit=math::ToDeg(AngleDeficit); AngleDeficit=math::ToDeg(AngleDeficit);
if ((AngleDeficit>45)||(AngleDeficit<0)) if ((AngleDeficit>45)||(AngleDeficit<0))
{ {
@ -297,7 +378,13 @@ public:
} }
// assert(AngleDeficit<45); // assert(AngleDeficit<45);
// assert(AngleDeficit>=0); // assert(AngleDeficit>=0);
m.face[i].Q()=(AngleDeficit)/(ScalarType)45;
ScalarType doubleArea = vcg::DoubleArea( f );
ScalarType distortion = (AngleDeficit)/ 45 ;
m.face[i].Q()= distortion;
tot += distortion * doubleArea;
totA += doubleArea;
} }
//finally restore the original directions //finally restore the original directions
@ -306,36 +393,77 @@ public:
m.face[i].PD1()=Dir1[i]; m.face[i].PD1()=Dir1[i];
m.face[i].PD2()=Dir2[i]; m.face[i].PD2()=Dir2[i];
} }
return tot / totA;
} }
static void SetQasDistorsion(MeshType &m, static ScalarType SetQasDistorsion(MeshType &m, DistType DType=AreaDist)
DistType DType=AreaDist)
{ {
if (DType==CrossDist) if (DType==CrossDist)
{ {
SetQasCrossDirDistortion(m); ScalarType res = SetFQAsCrossDirDistortion(m);
vcg::tri::UpdateQuality<MeshType>::VertexFromFace(m,true); vcg::tri::UpdateQuality<MeshType>::VertexFromFace(m,true);
return; return res;
} }
ScalarType edge_scale,area_scale; ScalarType edge_scale,area_scale;
MeshScalingFactor(m,area_scale,edge_scale); MeshScalingFactor(m,area_scale,edge_scale);
float tot = 0;
float totA = 0;
for (int i=0;i<m.face.size();i++) for (int i=0;i<m.face.size();i++)
{ {
if (m.face[i].IsD())continue; if (m.face[i].IsD())continue;
if (DType==AreaDist) ScalarType q;
m.face[i].Q()=AreaDistortion(&m.face[i],area_scale); switch (DType) {
else case CrossDist:
if (DType==AngleDist) // make compiler happy
m.face[i].Q()=AngleDistortion(&m.face[i]); q = 0;
else break;
m.face[i].Q()=(EdgeDistortion(&m.face[i],0,edge_scale)+ case AreaDist:
EdgeDistortion(&m.face[i],1,edge_scale)+ q = AreaDistortion(&m.face[i],area_scale);
EdgeDistortion(&m.face[i],2,edge_scale))/3.0; break;
case AngleDist:
q = AngleDistortion(&m.face[i]);
break;
case EdgeDist:
q =( EdgeDistortion(&m.face[i],0,edge_scale)+
EdgeDistortion(&m.face[i],1,edge_scale)+
EdgeDistortion(&m.face[i],2,edge_scale) )/3;
break;
case L2Stretch:
q = L2StretchEnergySquared( &m.face[i],area_scale );
break;
case LInfStretch:
q = LInfStretchEnergy( &m.face[i],area_scale );
break;
}
m.face[i].Q() = q; // note: for L2Stretch, we are puttning E^2 on Q
// aggregate:
if (DType==LInfStretch) {
tot = std::max( tot, q );
} else {
ScalarType a = Area3D(&m.face[i]);
tot += q*a;
totA += a;
}
} }
vcg::tri::UpdateQuality<MeshType>::VertexFromFace(m,true); vcg::tri::UpdateQuality<MeshType>::VertexFromFace(m,true);
switch (DType) {
case L2Stretch: return sqrt(tot/totA);
case LInfStretch: return tot;
default: return tot/totA;
}
} }
}; };
}
} }} // namespace end
#endif #endif