add vcglib
This commit is contained in:
1 parent
6f6c0c8eb7
commit
af8100f851
1537 files changed
+634197
No files matched your search
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_BOUNDING
|
||||
#define __VCG_TRI_UPDATE_BOUNDING
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
/// \headerfile bounding.h vcg/complex/algorithms/update/bounding.h
|
||||
/// \brief This class is used to compute or update the bounding box of a mesh..
|
||||
|
||||
template <class ComputeMeshType>
|
||||
class UpdateBounding
|
||||
{
|
||||
|
||||
public:
|
||||
typedef ComputeMeshType MeshType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
|
||||
/// \brief Calculates the bounding box of the given mesh m
|
||||
|
||||
static void Box(ComputeMeshType &m)
|
||||
{
|
||||
m.bbox.SetNull();
|
||||
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
if( !(*vi).IsD() ) m.bbox.Add((*vi).cP());
|
||||
}
|
||||
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,89 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_EDGES
|
||||
#define __VCG_TRI_UPDATE_EDGES
|
||||
|
||||
#include <vcg/space/plane3.h>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \brief This class is used to compute or update the precomputed data used to efficiently compute point-face distances.
|
||||
template <class ComputeMeshType>
|
||||
class UpdateComponentEP
|
||||
{
|
||||
|
||||
public:
|
||||
typedef ComputeMeshType MeshType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::FaceType::CoordType::ScalarType ScalarType;
|
||||
|
||||
static void ComputeEdgePlane(FaceType &f)
|
||||
{
|
||||
f.Flags() = f.Flags() & (~(FaceType::NORMX|FaceType::NORMY|FaceType::NORMZ));
|
||||
|
||||
// Primo calcolo degli edges
|
||||
f.Edge(0) = f.V(1)->P(); f.Edge(0) -= f.V(0)->P();
|
||||
f.Edge(1) = f.V(2)->P(); f.Edge(1) -= f.V(1)->P();
|
||||
f.Edge(2) = f.V(0)->P(); f.Edge(2) -= f.V(2)->P();
|
||||
// Calcolo di plane
|
||||
f.Plane().SetDirection(f.Edge(0)^f.Edge(1));
|
||||
f.Plane().SetOffset(f.Plane().Direction().dot(f.V(0)->P()));
|
||||
f.Plane().Normalize();
|
||||
// Calcolo migliore proiezione
|
||||
ScalarType nx = math::Abs(f.Plane().Direction()[0]);
|
||||
ScalarType ny = math::Abs(f.Plane().Direction()[1]);
|
||||
ScalarType nz = math::Abs(f.Plane().Direction()[2]);
|
||||
ScalarType d;
|
||||
if(nx>ny && nx>nz) { f.Flags() |= FaceType::NORMX; d = 1/f.Plane().Direction()[0]; }
|
||||
else if(ny>nz) { f.Flags() |= FaceType::NORMY; d = 1/f.Plane().Direction()[1]; }
|
||||
else { f.Flags() |= FaceType::NORMZ; d = 1/f.Plane().Direction()[2]; }
|
||||
|
||||
// Scalatura spigoli
|
||||
f.Edge(0)*=d;
|
||||
f.Edge(1)*=d;
|
||||
f.Edge(2)*=d;
|
||||
}
|
||||
|
||||
static void Set(ComputeMeshType &m)
|
||||
{
|
||||
if(!FaceType::HasEdgePlane()) throw vcg::MissingComponentException("PerFaceEdgePlane");
|
||||
for(FaceIterator f = m.face.begin(); f!=m.face.end(); ++f)
|
||||
if(!(*f).IsD())
|
||||
ComputeEdgePlane(*f);
|
||||
}
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,733 @@
|
||||
/****************************************************************************
|
||||
* 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_UPDATE_CURVATURE_
|
||||
#define VCGLIB_UPDATE_CURVATURE_
|
||||
|
||||
#include <vcg/space/index/grid_static_ptr.h>
|
||||
#include <vcg/simplex/face/jumping_pos.h>
|
||||
#include <vcg/complex/algorithms/update/normal.h>
|
||||
#include <vcg/complex/algorithms/point_sampling.h>
|
||||
#include <vcg/complex/algorithms/intersection.h>
|
||||
#include <vcg/complex/algorithms/inertia.h>
|
||||
#include <Eigen/Core>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile curvature.h vcg/complex/algorithms/update/curvature.h
|
||||
|
||||
/// \brief Management, updating and computation of per-vertex and per-face normals.
|
||||
/**
|
||||
This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh.
|
||||
*/
|
||||
|
||||
template <class MeshType>
|
||||
class UpdateCurvature
|
||||
{
|
||||
|
||||
public:
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::VertContainer VertContainer;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef vcg::face::VFIterator<FaceType> VFIteratorType;
|
||||
typedef typename MeshType::CoordType CoordType;
|
||||
typedef typename CoordType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexType::CurScalarType CurScalarType;
|
||||
typedef typename MeshType::VertexType::CurVecType CurVecType;
|
||||
|
||||
|
||||
private:
|
||||
// aux data struct used by PrincipalDirections
|
||||
struct AdjVertex {
|
||||
VertexType * vert;
|
||||
float doubleArea;
|
||||
bool isBorder;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
/// \brief Compute principal direction and magnitudo of curvature.
|
||||
|
||||
/*
|
||||
Compute principal direction and magniuto of curvature as describe in the paper:
|
||||
@InProceedings{bb33922,
|
||||
author = "G. Taubin",
|
||||
title = "Estimating the Tensor of Curvature of a Surface from a
|
||||
Polyhedral Approximation",
|
||||
booktitle = "International Conference on Computer Vision",
|
||||
year = "1995",
|
||||
pages = "902--907",
|
||||
URL = "http://dx.doi.org/10.1109/ICCV.1995.466840",
|
||||
bibsource = "http://www.visionbib.com/bibliography/describe440.html#TT32253",
|
||||
*/
|
||||
static void PrincipalDirections(MeshType &m)
|
||||
{
|
||||
tri::RequireVFAdjacency(m);
|
||||
vcg::tri::UpdateNormal<MeshType>::PerVertexAngleWeighted(m);
|
||||
vcg::tri::UpdateNormal<MeshType>::NormalizePerVertex(m);
|
||||
|
||||
for (VertexIterator vi =m.vert.begin(); vi !=m.vert.end(); ++vi) {
|
||||
if ( ! (*vi).IsD() && (*vi).VFp() != NULL) {
|
||||
|
||||
VertexType * central_vertex = &(*vi);
|
||||
|
||||
std::vector<float> weights;
|
||||
std::vector<AdjVertex> vertices;
|
||||
|
||||
vcg::face::JumpingPos<FaceType> pos((*vi).VFp(), central_vertex);
|
||||
|
||||
// firstV is the first vertex of the 1ring neighboorhood
|
||||
VertexType* firstV = pos.VFlip();
|
||||
VertexType* tempV;
|
||||
float totalDoubleAreaSize = 0.0f;
|
||||
|
||||
// compute the area of each triangle around the central vertex as well as their total area
|
||||
do
|
||||
{
|
||||
// this bring the pos to the next triangle counterclock-wise
|
||||
pos.FlipF();
|
||||
pos.FlipE();
|
||||
|
||||
// tempV takes the next vertex in the 1ring neighborhood
|
||||
tempV = pos.VFlip();
|
||||
assert(tempV!=central_vertex);
|
||||
AdjVertex v;
|
||||
|
||||
v.isBorder = pos.IsBorder();
|
||||
v.vert = tempV;
|
||||
v.doubleArea = vcg::DoubleArea(*pos.F());
|
||||
totalDoubleAreaSize += v.doubleArea;
|
||||
|
||||
vertices.push_back(v);
|
||||
}
|
||||
while(tempV != firstV);
|
||||
|
||||
// compute the weights for the formula computing matrix M
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
if (vertices[i].isBorder) {
|
||||
weights.push_back(vertices[i].doubleArea / totalDoubleAreaSize);
|
||||
} else {
|
||||
weights.push_back(0.5f * (vertices[i].doubleArea + vertices[(i-1)%vertices.size()].doubleArea) / totalDoubleAreaSize);
|
||||
}
|
||||
assert(weights.back() < 1.0f);
|
||||
}
|
||||
|
||||
// compute I-NN^t to be used for computing the T_i's
|
||||
Matrix33<ScalarType> Tp;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
Tp[i][i] = 1.0f - powf(central_vertex->cN()[i],2);
|
||||
Tp[0][1] = Tp[1][0] = -1.0f * (central_vertex->N()[0] * central_vertex->cN()[1]);
|
||||
Tp[1][2] = Tp[2][1] = -1.0f * (central_vertex->cN()[1] * central_vertex->cN()[2]);
|
||||
Tp[0][2] = Tp[2][0] = -1.0f * (central_vertex->cN()[0] * central_vertex->cN()[2]);
|
||||
|
||||
// for all neighbors vi compute the directional curvatures k_i and the T_i
|
||||
// compute M by summing all w_i k_i T_i T_i^t
|
||||
Matrix33<ScalarType> tempMatrix;
|
||||
Matrix33<ScalarType> M;
|
||||
M.SetZero();
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
CoordType edge = (central_vertex->cP() - vertices[i].vert->cP());
|
||||
float curvature = (2.0f * (central_vertex->cN().dot(edge)) ) / edge.SquaredNorm();
|
||||
CoordType T = (Tp*edge).normalized();
|
||||
tempMatrix.ExternalProduct(T,T);
|
||||
M += tempMatrix * weights[i] * curvature ;
|
||||
}
|
||||
|
||||
// compute vector W for the Householder matrix
|
||||
CoordType W;
|
||||
CoordType e1(1.0f,0.0f,0.0f);
|
||||
if ((e1 - central_vertex->cN()).SquaredNorm() > (e1 + central_vertex->cN()).SquaredNorm())
|
||||
W = e1 - central_vertex->cN();
|
||||
else
|
||||
W = e1 + central_vertex->cN();
|
||||
W.Normalize();
|
||||
|
||||
// compute the Householder matrix I - 2WW^t
|
||||
Matrix33<ScalarType> Q;
|
||||
Q.SetIdentity();
|
||||
tempMatrix.ExternalProduct(W,W);
|
||||
Q -= tempMatrix * 2.0f;
|
||||
|
||||
// compute matrix Q^t M Q
|
||||
Matrix33<ScalarType> QtMQ = (Q.transpose() * M * Q);
|
||||
|
||||
// CoordType bl = Q.GetColumn(0);
|
||||
CoordType T1 = Q.GetColumn(1);
|
||||
CoordType T2 = Q.GetColumn(2);
|
||||
|
||||
// find sin and cos for the Givens rotation
|
||||
float s,c;
|
||||
// Gabriel Taubin hint and Valentino Fiorin impementation
|
||||
float alpha = QtMQ[1][1]-QtMQ[2][2];
|
||||
float beta = QtMQ[2][1];
|
||||
|
||||
float h[2];
|
||||
float delta = sqrtf(4.0f*powf(alpha, 2) +16.0f*powf(beta, 2));
|
||||
h[0] = (2.0f*alpha + delta) / (2.0f*beta);
|
||||
h[1] = (2.0f*alpha - delta) / (2.0f*beta);
|
||||
|
||||
float t[2];
|
||||
float best_c, best_s;
|
||||
float min_error = std::numeric_limits<ScalarType>::infinity();
|
||||
for (int i=0; i<2; i++)
|
||||
{
|
||||
delta = sqrtf(powf(h[i], 2) + 4.0f);
|
||||
t[0] = (h[i]+delta) / 2.0f;
|
||||
t[1] = (h[i]-delta) / 2.0f;
|
||||
|
||||
for (int j=0; j<2; j++)
|
||||
{
|
||||
float squared_t = powf(t[j], 2);
|
||||
float denominator = 1.0f + squared_t;
|
||||
s = (2.0f*t[j]) / denominator;
|
||||
c = (1-squared_t) / denominator;
|
||||
|
||||
float approximation = c*s*alpha + (powf(c, 2) - powf(s, 2))*beta;
|
||||
float angle_similarity = fabs(acosf(c)/asinf(s));
|
||||
float error = fabs(1.0f-angle_similarity)+fabs(approximation);
|
||||
if (error<min_error)
|
||||
{
|
||||
min_error = error;
|
||||
best_c = c;
|
||||
best_s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
c = best_c;
|
||||
s = best_s;
|
||||
|
||||
Eigen::Matrix2f minor2x2;
|
||||
Eigen::Matrix2f S;
|
||||
|
||||
|
||||
// diagonalize M
|
||||
minor2x2(0,0) = QtMQ[1][1];
|
||||
minor2x2(0,1) = QtMQ[1][2];
|
||||
minor2x2(1,0) = QtMQ[2][1];
|
||||
minor2x2(1,1) = QtMQ[2][2];
|
||||
|
||||
S(0,0) = S(1,1) = c;
|
||||
S(0,1) = s;
|
||||
S(1,0) = -1.0f * s;
|
||||
|
||||
Eigen::Matrix2f StMS = S.transpose() * minor2x2 * S;
|
||||
|
||||
// compute curvatures and curvature directions
|
||||
float Principal_Curvature1 = (3.0f * StMS(0,0)) - StMS(1,1);
|
||||
float Principal_Curvature2 = (3.0f * StMS(1,1)) - StMS(0,0);
|
||||
|
||||
CoordType Principal_Direction1 = T1 * c - T2 * s;
|
||||
CoordType Principal_Direction2 = T1 * s + T2 * c;
|
||||
|
||||
(*vi).PD1().Import(Principal_Direction1);
|
||||
(*vi).PD2().Import(Principal_Direction2);
|
||||
(*vi).K1() = Principal_Curvature1;
|
||||
(*vi).K2() = Principal_Curvature2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class AreaData
|
||||
{
|
||||
public:
|
||||
float A;
|
||||
};
|
||||
|
||||
/** Curvature meseaure as described in the paper:
|
||||
Robust principal curvatures on Multiple Scales, Yong-Liang Yang, Yu-Kun Lai, Shi-Min Hu Helmut Pottmann
|
||||
SGP 2004
|
||||
If pointVSfaceInt==true the covariance is computed by montecarlo sampling on the mesh (faster)
|
||||
If pointVSfaceInt==false the covariance is computed by (analytic)integration over the surface (slower)
|
||||
*/
|
||||
|
||||
typedef vcg::GridStaticPtr <FaceType, ScalarType > MeshGridType;
|
||||
typedef vcg::GridStaticPtr <VertexType, ScalarType > PointsGridType;
|
||||
|
||||
static void PrincipalDirectionsPCA(MeshType &m, ScalarType r, bool pointVSfaceInt = true,vcg::CallBackPos * cb = NULL)
|
||||
{
|
||||
std::vector<VertexType*> closests;
|
||||
std::vector<ScalarType> distances;
|
||||
std::vector<CoordType> points;
|
||||
VertexIterator vi;
|
||||
ScalarType area;
|
||||
MeshType tmpM;
|
||||
typename std::vector<CoordType>::iterator ii;
|
||||
vcg::tri::TrivialSampler<MeshType> vs;
|
||||
tri::UpdateNormal<MeshType>::PerVertexAngleWeighted(m);
|
||||
tri::UpdateNormal<MeshType>::NormalizePerVertex(m);
|
||||
|
||||
MeshGridType mGrid;
|
||||
PointsGridType pGrid;
|
||||
|
||||
// Fill the grid used
|
||||
if(pointVSfaceInt)
|
||||
{
|
||||
area = Stat<MeshType>::ComputeMeshArea(m);
|
||||
vcg::tri::SurfaceSampling<MeshType,vcg::tri::TrivialSampler<MeshType> >::Montecarlo(m,vs,1000 * area / (2*M_PI*r*r ));
|
||||
vi = vcg::tri::Allocator<MeshType>::AddVertices(tmpM,m.vert.size());
|
||||
for(size_t y = 0; y < m.vert.size(); ++y,++vi) (*vi).P() = m.vert[y].P();
|
||||
pGrid.Set(tmpM.vert.begin(),tmpM.vert.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
mGrid.Set(m.face.begin(),m.face.end());
|
||||
}
|
||||
|
||||
int jj = 0;
|
||||
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
{
|
||||
vcg::Matrix33<ScalarType> A, eigenvectors;
|
||||
vcg::Point3<ScalarType> bp, eigenvalues;
|
||||
// int nrot;
|
||||
|
||||
// sample the neighborhood
|
||||
if(pointVSfaceInt)
|
||||
{
|
||||
vcg::tri::GetInSphereVertex<
|
||||
MeshType,
|
||||
PointsGridType,std::vector<VertexType*>,
|
||||
std::vector<ScalarType>,
|
||||
std::vector<CoordType> >(tmpM,pGrid, (*vi).cP(),r ,closests,distances,points);
|
||||
|
||||
A.Covariance(points,bp);
|
||||
A*=area*area/1000;
|
||||
}
|
||||
else{
|
||||
IntersectionBallMesh<MeshType,ScalarType>( m ,vcg::Sphere3<ScalarType>((*vi).cP(),r),tmpM );
|
||||
vcg::Point3<ScalarType> _bary;
|
||||
vcg::tri::Inertia<MeshType>::Covariance(tmpM,_bary,A);
|
||||
}
|
||||
|
||||
// Eigen::Matrix3f AA; AA=A;
|
||||
// Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eig(AA);
|
||||
// Eigen::Vector3f c_val = eig.eigenvalues();
|
||||
// Eigen::Matrix3f c_vec = eig.eigenvectors();
|
||||
|
||||
// Jacobi(A, eigenvalues , eigenvectors, nrot);
|
||||
|
||||
Eigen::Matrix3d AA;
|
||||
A.ToEigenMatrix(AA);
|
||||
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(AA);
|
||||
Eigen::Vector3d c_val = eig.eigenvalues();
|
||||
Eigen::Matrix3d c_vec = eig.eigenvectors(); // eigenvector are stored as columns.
|
||||
eigenvectors.FromEigenMatrix(c_vec);
|
||||
eigenvalues.FromEigenVector(c_val);
|
||||
// EV.transposeInPlace();
|
||||
// ev.FromEigenVector(c_val);
|
||||
|
||||
// get the estimate of curvatures from eigenvalues and eigenvectors
|
||||
// find the 2 most tangent eigenvectors (by finding the one closest to the normal)
|
||||
int best = 0; ScalarType bestv = fabs( (*vi).cN().dot(eigenvectors.GetColumn(0).normalized()) );
|
||||
for(int i = 1 ; i < 3; ++i){
|
||||
ScalarType prod = fabs((*vi).cN().dot(eigenvectors.GetColumn(i).normalized()));
|
||||
if( prod > bestv){bestv = prod; best = i;}
|
||||
}
|
||||
|
||||
(*vi).PD1().Import(eigenvectors.GetColumn( (best+1)%3).normalized());
|
||||
(*vi).PD2().Import(eigenvectors.GetColumn( (best+2)%3).normalized());
|
||||
|
||||
// project them to the plane identified by the normal
|
||||
vcg::Matrix33<CurScalarType> rot;
|
||||
CurVecType NN = CurVecType::Construct((*vi).N());
|
||||
CurScalarType angle;
|
||||
angle = acos((*vi).PD1().dot(NN));
|
||||
rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD1()^NN);
|
||||
(*vi).PD1() = rot*(*vi).PD1();
|
||||
angle = acos((*vi).PD2().dot(NN));
|
||||
rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD2()^NN);
|
||||
(*vi).PD2() = rot*(*vi).PD2();
|
||||
|
||||
|
||||
// copmutes the curvature values
|
||||
const ScalarType r5 = r*r*r*r*r;
|
||||
const ScalarType r6 = r*r5;
|
||||
(*vi).K1() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+2)%3]-45.0*eigenvalues[(best+1)%3])/(M_PI*r6);
|
||||
(*vi).K2() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+1)%3]-45.0*eigenvalues[(best+2)%3])/(M_PI*r6);
|
||||
if((*vi).K1() < (*vi).K2()) { std::swap((*vi).K1(),(*vi).K2());
|
||||
std::swap((*vi).PD1(),(*vi).PD2());
|
||||
if (cb)
|
||||
{
|
||||
(*cb)(int(100.0f * (float)jj / (float)m.vn),"Vertices Analysis");
|
||||
++jj;
|
||||
} }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// \brief Computes the discrete mean gaussian curvature.
|
||||
/**
|
||||
The algorithm used is the one Desbrun et al. that is based on a discrete analysis of the angles of the faces around a vertex.
|
||||
|
||||
It requires FaceFace Adjacency;
|
||||
|
||||
For further details, please, refer to: \n
|
||||
|
||||
<b>Discrete Differential-Geometry Operators for Triangulated 2-Manifolds </b><br>
|
||||
<i>Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr</i><br>
|
||||
VisMath '02, Berlin
|
||||
*/
|
||||
static void MeanAndGaussian(MeshType & m)
|
||||
{
|
||||
tri::RequireFFAdjacency(m);
|
||||
|
||||
float area0, area1, area2, angle0, angle1, angle2;
|
||||
FaceIterator fi;
|
||||
VertexIterator vi;
|
||||
typename MeshType::CoordType e01v ,e12v ,e20v;
|
||||
|
||||
SimpleTempData<VertContainer, AreaData> TDAreaPtr(m.vert);
|
||||
SimpleTempData<VertContainer, typename MeshType::CoordType> TDContr(m.vert);
|
||||
|
||||
vcg::tri::UpdateNormal<MeshType>::PerVertexNormalized(m);
|
||||
auto KH = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KH"));
|
||||
auto KG = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KG"));
|
||||
|
||||
//Compute AreaMix in H (vale anche per K)
|
||||
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD())
|
||||
{
|
||||
(TDAreaPtr)[*vi].A = 0.0;
|
||||
(TDContr)[*vi] =typename MeshType::CoordType(0.0,0.0,0.0);
|
||||
KH[*vi] = 0.0;
|
||||
KG[*vi] = (ScalarType)(2.0 * M_PI);
|
||||
}
|
||||
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD())
|
||||
{
|
||||
// angles
|
||||
angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
|
||||
angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
|
||||
angle2 = M_PI-(angle0+angle1);
|
||||
|
||||
if((angle0 < M_PI/2) && (angle1 < M_PI/2) && (angle2 < M_PI/2)) // triangolo non ottuso
|
||||
{
|
||||
float e01 = SquaredDistance( (*fi).V(1)->cP() , (*fi).V(0)->cP() );
|
||||
float e12 = SquaredDistance( (*fi).V(2)->cP() , (*fi).V(1)->cP() );
|
||||
float e20 = SquaredDistance( (*fi).V(0)->cP() , (*fi).V(2)->cP() );
|
||||
|
||||
area0 = ( e20*(1.0/tan(angle1)) + e01*(1.0/tan(angle2)) ) / 8.0;
|
||||
area1 = ( e01*(1.0/tan(angle2)) + e12*(1.0/tan(angle0)) ) / 8.0;
|
||||
area2 = ( e12*(1.0/tan(angle0)) + e20*(1.0/tan(angle1)) ) / 8.0;
|
||||
|
||||
(TDAreaPtr)[(*fi).V(0)].A += area0;
|
||||
(TDAreaPtr)[(*fi).V(1)].A += area1;
|
||||
(TDAreaPtr)[(*fi).V(2)].A += area2;
|
||||
|
||||
}
|
||||
else // obtuse
|
||||
{
|
||||
if(angle0 >= M_PI/2)
|
||||
{
|
||||
(TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
|
||||
(TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
|
||||
(TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
|
||||
}
|
||||
else if(angle1 >= M_PI/2)
|
||||
{
|
||||
(TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
|
||||
(TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
|
||||
(TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
(TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
|
||||
(TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
|
||||
(TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD() )
|
||||
{
|
||||
angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
|
||||
angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
|
||||
angle2 = M_PI-(angle0+angle1);
|
||||
|
||||
// Skip degenerate triangles.
|
||||
if(angle0==0 || angle1==0 || angle2==0) continue;
|
||||
|
||||
e01v = ( (*fi).V(1)->cP() - (*fi).V(0)->cP() ) ;
|
||||
e12v = ( (*fi).V(2)->cP() - (*fi).V(1)->cP() ) ;
|
||||
e20v = ( (*fi).V(0)->cP() - (*fi).V(2)->cP() ) ;
|
||||
|
||||
TDContr[(*fi).V(0)] += ( e20v * (1.0/tan(angle1)) - e01v * (1.0/tan(angle2)) ) / 4.0;
|
||||
TDContr[(*fi).V(1)] += ( e01v * (1.0/tan(angle2)) - e12v * (1.0/tan(angle0)) ) / 4.0;
|
||||
TDContr[(*fi).V(2)] += ( e12v * (1.0/tan(angle0)) - e20v * (1.0/tan(angle1)) ) / 4.0;
|
||||
|
||||
KG[(*fi).V(0)] -= angle0;
|
||||
KG[(*fi).V(1)] -= angle1;
|
||||
KG[(*fi).V(2)] -= angle2;
|
||||
|
||||
|
||||
for(int i=0;i<3;i++)
|
||||
{
|
||||
if(vcg::face::IsBorder((*fi), i))
|
||||
{
|
||||
CoordType e1,e2;
|
||||
vcg::face::Pos<FaceType> hp(&*fi, i, (*fi).V(i));
|
||||
vcg::face::Pos<FaceType> hp1=hp;
|
||||
|
||||
hp1.FlipV();
|
||||
e1=hp1.v->cP() - hp.v->cP();
|
||||
hp1.FlipV();
|
||||
hp1.NextB();
|
||||
e2=hp1.v->cP() - hp.v->cP();
|
||||
KG[(*fi).V(i)] -= math::Abs(Angle(e1,e2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/)
|
||||
{
|
||||
if((TDAreaPtr)[*vi].A<=std::numeric_limits<ScalarType>::epsilon())
|
||||
{
|
||||
KH[(*vi)] = 0;
|
||||
KG[(*vi)] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
KH[(*vi)] = (((TDContr)[*vi].dot((*vi).cN())>0)?1.0:-1.0)*((TDContr)[*vi] / (TDAreaPtr) [*vi].A).Norm();
|
||||
KG[(*vi)] /= (TDAreaPtr)[*vi].A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \brief Update the mean and the gaussian curvature of a vertex.
|
||||
|
||||
/**
|
||||
The function uses the VF adiacency to walk around the vertex.
|
||||
|
||||
Based on the paper <a href="http://www2.in.tu-clausthal.de/~hormann/papers/Dyn.2001.OTU.pdf">
|
||||
<em> "Optimizing 3d triangulations using discrete curvature analysis" </em> </a>
|
||||
it compute an approximation of the gaussian and of the absolute mean curvature
|
||||
*/
|
||||
static void PerVertexAbsoluteMeanAndGaussian(MeshType & m)
|
||||
{
|
||||
tri::RequireVFAdjacency(m);
|
||||
tri::RequireCompactness(m);
|
||||
const bool areaNormalize = true;
|
||||
const bool barycentricArea=false;
|
||||
auto KH = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KH"));
|
||||
auto KG = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KG"));
|
||||
int faceCnt=0;
|
||||
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
{
|
||||
VertexPointer v=&*vi;
|
||||
VFIteratorType vfi(v);
|
||||
ScalarType A = 0;
|
||||
|
||||
KH[v] = 0;
|
||||
ScalarType AngleDefect = (ScalarType)(2.0 * M_PI);;
|
||||
|
||||
while (!vfi.End()) {
|
||||
faceCnt++;
|
||||
FacePointer f = vfi.F();
|
||||
CoordType nf = TriangleNormal(*f);
|
||||
int i = vfi.I();
|
||||
VertexPointer v0 = f->V0(i), v1 = f->V1(i), v2 = f->V2(i);
|
||||
assert (v==v0);
|
||||
|
||||
ScalarType ang0 = math::Abs(Angle(v1->P() - v0->P(), v2->P() - v0->P() ));
|
||||
ScalarType ang1 = math::Abs(Angle(v0->P() - v1->P(), v2->P() - v1->P() ));
|
||||
ScalarType ang2 = M_PI - ang0 - ang1;
|
||||
|
||||
ScalarType s01 = SquaredDistance(v1->P(), v0->P());
|
||||
ScalarType s02 = SquaredDistance(v2->P(), v0->P());
|
||||
|
||||
// voronoi cell of current vertex
|
||||
if(barycentricArea)
|
||||
A+=vcg::DoubleArea(*f)/6.0;
|
||||
else
|
||||
{
|
||||
if (ang0 >= M_PI/2)
|
||||
A += (0.5f * DoubleArea(*f) - (s01 * tan(ang1) + s02 * tan(ang2)) / 8.0 );
|
||||
else if (ang1 >= M_PI/2)
|
||||
A += (s01 * tan(ang0)) / 8.0;
|
||||
else if (ang2 >= M_PI/2)
|
||||
A += (s02 * tan(ang0)) / 8.0;
|
||||
else // non obctuse triangle
|
||||
A += ((s02 / tan(ang1)) + (s01 / tan(ang2))) / 8.0;
|
||||
}
|
||||
// gaussian curvature update
|
||||
AngleDefect -= ang0;
|
||||
|
||||
// mean curvature update
|
||||
// Note that the standard abs mean curvature approximation would require
|
||||
// to sum all the edges*diehedralAngle. Here with just VF adjacency
|
||||
// we make a rough approximation that 1/2 of the edge len plus something
|
||||
// that is half of the diedral angle
|
||||
ang1 = math::Abs(Angle(nf, v1->N()+v0->N()));
|
||||
ang2 = math::Abs(Angle(nf, v2->N()+v0->N()));
|
||||
KH[v] += math::Sqrt(s01)*ang1 + math::Sqrt(s02)*ang2 ;
|
||||
++vfi;
|
||||
}
|
||||
|
||||
KH[v] /= 4.0;
|
||||
|
||||
if(areaNormalize) {
|
||||
if(A <= std::numeric_limits<float>::epsilon()) {
|
||||
KH[v] = 0;
|
||||
KG[v] = 0;
|
||||
}
|
||||
else {
|
||||
KH[v] /= A;
|
||||
KG[v] = AngleDefect / A;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Compute principal curvature directions and value with normal cycle:
|
||||
@inproceedings{CohMor03,
|
||||
author = {Cohen-Steiner, David and Morvan, Jean-Marie },
|
||||
booktitle = {SCG '03: Proceedings of the nineteenth annual symposium on Computational geometry},
|
||||
title - {Restricted delaunay triangulations and normal cycle}
|
||||
year = {2003}
|
||||
}
|
||||
*/
|
||||
|
||||
static void PrincipalDirectionsNormalCycle(MeshType & m){
|
||||
tri::RequireVFAdjacency(m);
|
||||
tri::RequireFFAdjacency(m);
|
||||
|
||||
typename MeshType::VertexIterator vi;
|
||||
|
||||
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
if(!((*vi).IsD())){
|
||||
vcg::Matrix33<ScalarType> m33;m33.SetZero();
|
||||
face::JumpingPos<typename MeshType::FaceType> p((*vi).VFp(),&(*vi));
|
||||
p.FlipE();
|
||||
typename MeshType::VertexType * firstv = p.VFlip();
|
||||
assert(p.F()->V(p.VInd())==&(*vi));
|
||||
|
||||
do{
|
||||
if( p.F() != p.FFlip()){
|
||||
Point3<ScalarType> normalized_edge = p.F()->V(p.F()->Next(p.VInd()))->cP() - (*vi).P();
|
||||
ScalarType edge_length = normalized_edge.Norm();
|
||||
normalized_edge/=edge_length;
|
||||
Point3<ScalarType> n1 = NormalizedTriangleNormal(*(p.F()));
|
||||
Point3<ScalarType> n2 = NormalizedTriangleNormal(*(p.FFlip()));
|
||||
ScalarType n1n2 = (n1 ^ n2).dot(normalized_edge);
|
||||
n1n2 = std::max(std::min( ScalarType(1.0),n1n2),ScalarType(-1.0));
|
||||
ScalarType beta = math::Asin(n1n2);
|
||||
m33[0][0] += beta*edge_length*normalized_edge[0]*normalized_edge[0];
|
||||
m33[0][1] += beta*edge_length*normalized_edge[1]*normalized_edge[0];
|
||||
m33[1][1] += beta*edge_length*normalized_edge[1]*normalized_edge[1];
|
||||
m33[0][2] += beta*edge_length*normalized_edge[2]*normalized_edge[0];
|
||||
m33[1][2] += beta*edge_length*normalized_edge[2]*normalized_edge[1];
|
||||
m33[2][2] += beta*edge_length*normalized_edge[2]*normalized_edge[2];
|
||||
}
|
||||
p.NextFE();
|
||||
}while(firstv != p.VFlip());
|
||||
|
||||
if(m33.Determinant()==0.0){ // degenerate case
|
||||
(*vi).K1() = (*vi).K2() = 0.0; continue;}
|
||||
|
||||
m33[1][0] = m33[0][1];
|
||||
m33[2][0] = m33[0][2];
|
||||
m33[2][1] = m33[1][2];
|
||||
|
||||
Eigen::Matrix3d it;
|
||||
m33.ToEigenMatrix(it);
|
||||
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(it);
|
||||
Eigen::Vector3d c_val = eig.eigenvalues();
|
||||
Eigen::Matrix3d c_vec = eig.eigenvectors();
|
||||
|
||||
Point3<ScalarType> lambda;
|
||||
Matrix33<ScalarType> vect;
|
||||
vect.FromEigenMatrix(c_vec);
|
||||
lambda.FromEigenVector(c_val);
|
||||
|
||||
ScalarType bestNormal = 0;
|
||||
int bestNormalIndex = -1;
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
float agreeWithNormal = fabs((*vi).N().Normalize().dot(vect.GetColumn(i)));
|
||||
if( agreeWithNormal > bestNormal )
|
||||
{
|
||||
bestNormal= agreeWithNormal;
|
||||
bestNormalIndex = i;
|
||||
}
|
||||
}
|
||||
int maxI = (bestNormalIndex+2)%3;
|
||||
int minI = (bestNormalIndex+1)%3;
|
||||
if(fabs(lambda[maxI]) < fabs(lambda[minI])) std::swap(maxI,minI);
|
||||
|
||||
(*vi).PD1().Import(vect.GetColumn(maxI));
|
||||
(*vi).PD2().Import(vect.GetColumn(minI));
|
||||
(*vi).K1() = lambda[2];
|
||||
(*vi).K2() = lambda[1];
|
||||
}
|
||||
}
|
||||
|
||||
static void PerVertexBasicRadialCrossField(MeshType &m, float anisotropyRatio = 1.0 )
|
||||
{
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
CoordType c=m.bbox.Center();
|
||||
float maxRad = m.bbox.Diag()/2.0f;
|
||||
|
||||
for(size_t i=0;i<m.vert.size();++i) {
|
||||
CoordType dd = m.vert[i].P()-c;
|
||||
dd.Normalize();
|
||||
m.vert[i].PD1().Import(dd^m.vert[i].N());
|
||||
m.vert[i].PD1().Normalize();
|
||||
m.vert[i].PD2().Import(m.vert[i].N()^CoordType::Construct(m.vert[i].PD1()));
|
||||
m.vert[i].PD2().Normalize();
|
||||
// Now the anisotropy
|
||||
// the idea is that the ratio between the two direction is at most <anisotropyRatio>
|
||||
// eg |PD1|/|PD2| < ratio
|
||||
// and |PD1|^2 + |PD2|^2 == 1
|
||||
|
||||
float q =Distance(m.vert[i].P(),c) / maxRad; // it is in the 0..1 range
|
||||
const float minRatio = 1.0f/anisotropyRatio;
|
||||
const float maxRatio = anisotropyRatio;
|
||||
const float curRatio = minRatio + (maxRatio-minRatio)*q;
|
||||
float pd1Len = sqrt(1.0/(1+curRatio*curRatio));
|
||||
float pd2Len = curRatio * pd1Len;
|
||||
// assert(fabs(curRatio - pd2Len/pd1Len)<0.0000001);
|
||||
// assert(fabs(pd1Len*pd1Len + pd2Len*pd2Len - 1.0f)<0.0001);
|
||||
m.vert[i].PD1() *= pd1Len;
|
||||
m.vert[i].PD2() *= pd2Len;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // end namespace tri
|
||||
} // end namespace vcg
|
||||
#endif
|
||||
@@ -0,0 +1,675 @@
|
||||
/****************************************************************************
|
||||
* 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_UPDATE_CURVATURE_FITTING
|
||||
#define VCGLIB_UPDATE_CURVATURE_FITTING
|
||||
|
||||
#include <vcg/space/index/grid_static_ptr.h>
|
||||
#include <vcg/simplex/face/topology.h>
|
||||
#include <vcg/simplex/face/pos.h>
|
||||
#include <vcg/simplex/face/jumping_pos.h>
|
||||
#include <vcg/container/simple_temporary_data.h>
|
||||
#include <vcg/complex/algorithms/update/normal.h>
|
||||
#include <vcg/complex/algorithms/point_sampling.h>
|
||||
#include <vcg/complex/algorithms/intersection.h>
|
||||
#include <vcg/complex/algorithms/inertia.h>
|
||||
#include <vcg/complex/algorithms/nring.h>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/QR>
|
||||
#include <Eigen/LU>
|
||||
#include <Eigen/SVD>
|
||||
#include <Eigen/Eigenvalues>
|
||||
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile curvature_fitting.h vcg/complex/algorithms/update/curvature_fitting.h
|
||||
|
||||
/// \brief Computation of per-vertex directions and values of curvature.
|
||||
/**
|
||||
This class is used to compute the per-vertex directions and values of curvature using a quadric fitting method.
|
||||
*/
|
||||
|
||||
template <class MeshType>
|
||||
class UpdateCurvatureFitting
|
||||
{
|
||||
|
||||
public:
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::VertContainer VertContainer;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexPointer VertexTypeP;
|
||||
typedef vcg::face::VFIterator<FaceType> VFIteratorType;
|
||||
typedef typename MeshType::CoordType CoordType;
|
||||
typedef typename CoordType::ScalarType ScalarType;
|
||||
|
||||
class Quadric
|
||||
{
|
||||
public:
|
||||
|
||||
Quadric(double av, double bv, double cv, double dv, double ev)
|
||||
{
|
||||
a() = av;
|
||||
b() = bv;
|
||||
c() = cv;
|
||||
d() = dv;
|
||||
e() = ev;
|
||||
}
|
||||
|
||||
double& a() { return data[0];}
|
||||
double& b() { return data[1];}
|
||||
double& c() { return data[2];}
|
||||
double& d() { return data[3];}
|
||||
double& e() { return data[4];}
|
||||
|
||||
double data[5];
|
||||
|
||||
double evaluate(double u, double v)
|
||||
{
|
||||
return a()*u*u + b()*u*v + c()*v*v + d()*u + e()*v;
|
||||
}
|
||||
|
||||
double du(double u, double v)
|
||||
{
|
||||
return 2.0*a()*u + b()*v + d();
|
||||
}
|
||||
|
||||
double dv(double u, double v)
|
||||
{
|
||||
return 2.0*c()*v + b()*u + e();
|
||||
}
|
||||
|
||||
double duv(double /*u*/, double /*v*/)
|
||||
{
|
||||
return b();
|
||||
}
|
||||
|
||||
double duu(double /*u*/, double /*v*/)
|
||||
{
|
||||
return 2.0*a();
|
||||
}
|
||||
|
||||
double dvv(double /*u*/, double /*v*/)
|
||||
{
|
||||
return 2.0*c();
|
||||
}
|
||||
|
||||
static Quadric fit(std::vector<CoordType> VV)
|
||||
{
|
||||
assert(VV.size() >= 5);
|
||||
Eigen::MatrixXf A(VV.size(),5);
|
||||
Eigen::MatrixXf b(VV.size(),1);
|
||||
Eigen::MatrixXf sol(VV.size(),1);
|
||||
|
||||
for(unsigned int c=0; c < VV.size(); ++c)
|
||||
{
|
||||
double u = VV[c].X();
|
||||
double v = VV[c].Y();
|
||||
double n = VV[c].Z();
|
||||
|
||||
A(c,0) = u*u;
|
||||
A(c,1) = u*v;
|
||||
A(c,2) = v*v;
|
||||
A(c,3) = u;
|
||||
A(c,4) = v;
|
||||
|
||||
b(c,0) = n;
|
||||
}
|
||||
|
||||
sol = ((A.transpose()*A).inverse()*A.transpose())*b;
|
||||
return Quadric(sol(0,0),sol(1,0),sol(2,0),sol(3,0),sol(4,0));
|
||||
}
|
||||
};
|
||||
|
||||
static CoordType project(VertexType* v, VertexType* vp)
|
||||
{
|
||||
return vp->P() - (v->N() * ((vp->P() - v->P()) * v->N()));
|
||||
}
|
||||
|
||||
|
||||
static std::vector<CoordType> computeReferenceFrames(VertexTypeP vi)
|
||||
{
|
||||
vcg::face::VFIterator<FaceType> vfi(vi);
|
||||
|
||||
int i = (vfi.I()+1)%3;
|
||||
VertexTypeP vp = vfi.F()->V(i);
|
||||
|
||||
CoordType x = (project(&*vi,vp) - vi->P()).Normalize();
|
||||
//assert(fabs(x * vi->N()) < 0.1);
|
||||
|
||||
std::vector<CoordType> res(3);
|
||||
|
||||
res[0] = x;
|
||||
res[1] = (vi->N() ^ res[0]).Normalize();
|
||||
res[2] = (vi->N())/(vi->N()).Norm();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::set<CoordType> getSecondRing(VertexTypeP v)
|
||||
{
|
||||
std::set<VertexTypeP> ris;
|
||||
std::set<CoordType> coords;
|
||||
|
||||
vcg::face::VFIterator<FaceType> vvi(v);
|
||||
for(;!vvi.End();++vvi)
|
||||
{
|
||||
vcg::face::VFIterator<FaceType> vvi2(vvi.F()->V((vvi.I()+1)%3));
|
||||
for(;!vvi2.End();++vvi2)
|
||||
{
|
||||
ris.insert(vvi2.F()->V((vvi2.I()+1)%3));
|
||||
}
|
||||
}
|
||||
typename std::set<VertexTypeP>::iterator it;
|
||||
for(it = ris.begin(); it != ris.end(); ++it)
|
||||
coords.insert((*it)->P());
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
static Quadric fitQuadric(VertexTypeP v, std::vector<CoordType>& ref)
|
||||
{
|
||||
std::set<CoordType> ring = getSecondRing(v);
|
||||
if (ring.size() < 5)
|
||||
return Quadric(1,1,1,1,1);
|
||||
std::vector<CoordType> points;
|
||||
|
||||
typename std::set<CoordType>::iterator b = ring.begin();
|
||||
typename std::set<CoordType>::iterator e = ring.end();
|
||||
|
||||
while(b != e)
|
||||
{
|
||||
// vtang non e` il v tangente!!!
|
||||
CoordType vTang = *b - v->P();
|
||||
|
||||
double x = vTang * ref[0];
|
||||
double y = vTang * ref[1];
|
||||
double z = vTang * ref[2];
|
||||
points.push_back(CoordType(x,y,z));
|
||||
++b;
|
||||
}
|
||||
return Quadric::fit(points);
|
||||
}
|
||||
|
||||
|
||||
static void computeCurvature(MeshType & m)
|
||||
{
|
||||
Allocator<MeshType>::CompactVertexVector(m);
|
||||
tri::RequireCompactness(m);
|
||||
tri::RequireVFAdjacency(m);
|
||||
|
||||
vcg::tri::UpdateTopology<MeshType>::VertexFace(m);
|
||||
|
||||
vcg::tri::UpdateNormal<MeshType>::PerVertexAngleWeighted(m);
|
||||
vcg::tri::UpdateNormal<MeshType>::NormalizePerVertex(m);
|
||||
|
||||
|
||||
VertexIterator vi;
|
||||
for(vi = m.vert.begin(); vi!=m.vert.end(); ++vi )
|
||||
{
|
||||
std::vector<CoordType> ref = computeReferenceFrames(&*vi);
|
||||
|
||||
Quadric q = fitQuadric(&*vi,ref);
|
||||
double a = q.a();
|
||||
double b = q.b();
|
||||
double c = q.c();
|
||||
double d = q.d();
|
||||
double e = q.e();
|
||||
|
||||
double E = 1.0 + d*d;
|
||||
double F = d*e;
|
||||
double G = 1.0 + e*e;
|
||||
|
||||
CoordType n = CoordType(-d,-e,1.0).Normalize();
|
||||
|
||||
vi->N() = ref[0] * n[0] + ref[1] * n[1] + ref[2] * n[2];
|
||||
|
||||
double L = 2.0 * a * n.Z();
|
||||
double M = b * n.Z();
|
||||
double N = 2 * c * n.Z();
|
||||
|
||||
// ----------------- Eigen stuff
|
||||
Eigen::Matrix2d m;
|
||||
m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F;
|
||||
m = m / (E*G-F*F);
|
||||
Eigen::SelfAdjointEigenSolver<Eigen::Matrix2d> eig(m);
|
||||
|
||||
Eigen::Vector2d c_val = eig.eigenvalues();
|
||||
Eigen::Matrix2d c_vec = eig.eigenvectors();
|
||||
|
||||
c_val = -c_val;
|
||||
|
||||
CoordType v1, v2;
|
||||
v1[0] = c_vec(0,0);
|
||||
v1[1] = c_vec(0,1);
|
||||
v1[2] = 0;
|
||||
|
||||
v2[0] = c_vec(1,0);
|
||||
v2[1] = c_vec(1,1);
|
||||
v2[2] = 0;
|
||||
|
||||
v1 = v1.Normalize();
|
||||
v2 = v2.Normalize();
|
||||
|
||||
v1 = v1 * c_val[0];
|
||||
v2 = v2 * c_val[1];
|
||||
|
||||
CoordType v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2];
|
||||
CoordType v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2];
|
||||
|
||||
v1global.Normalize();
|
||||
v2global.Normalize();
|
||||
|
||||
if (c_val[0] > c_val[1])
|
||||
{
|
||||
(*vi).PD1().Import(v1global);
|
||||
(*vi).PD2().Import(v2global);
|
||||
(*vi).K1() = c_val[0];
|
||||
(*vi).K2() = c_val[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
(*vi).PD1().Import(v2global);
|
||||
(*vi).PD2().Import(v1global);
|
||||
(*vi).K1() = c_val[1];
|
||||
(*vi).K2() = c_val[0];
|
||||
}
|
||||
// ---- end Eigen stuff
|
||||
}
|
||||
}
|
||||
|
||||
// GG LOCAL CURVATURE
|
||||
|
||||
class QuadricLocal
|
||||
{
|
||||
public:
|
||||
|
||||
QuadricLocal ()
|
||||
{
|
||||
a() = b() = c() = d() = e() = 1.0;
|
||||
}
|
||||
|
||||
QuadricLocal (double av, double bv, double cv, double dv, double ev)
|
||||
{
|
||||
a() = av;
|
||||
b() = bv;
|
||||
c() = cv;
|
||||
d() = dv;
|
||||
e() = ev;
|
||||
}
|
||||
|
||||
double& a() { return data[0];}
|
||||
double& b() { return data[1];}
|
||||
double& c() { return data[2];}
|
||||
double& d() { return data[3];}
|
||||
double& e() { return data[4];}
|
||||
|
||||
double data[5];
|
||||
|
||||
double evaluate(double u, double v)
|
||||
{
|
||||
return a()*u*u + b()*u*v + c()*v*v + d()*u + e()*v;
|
||||
}
|
||||
|
||||
double du(double u, double v)
|
||||
{
|
||||
return 2.0*a()*u + b()*v + d();
|
||||
}
|
||||
|
||||
double dv(double u, double v)
|
||||
{
|
||||
return 2.0*c()*v + b()*u + e();
|
||||
}
|
||||
|
||||
double duv(double /*u*/, double /*v*/)
|
||||
{
|
||||
return b();
|
||||
}
|
||||
|
||||
double duu(double /*u*/, double /*v*/)
|
||||
{
|
||||
return 2.0*a();
|
||||
}
|
||||
|
||||
double dvv(double /*u*/, double /*v*/)
|
||||
{
|
||||
return 2.0*c();
|
||||
}
|
||||
|
||||
|
||||
static QuadricLocal fit(std::vector<CoordType> &VV, bool svdRes, bool detCheck)
|
||||
{
|
||||
assert(VV.size() >= 5);
|
||||
Eigen::MatrixXd A(VV.size(),5);
|
||||
Eigen::VectorXd b(VV.size());
|
||||
Eigen::VectorXd sol(5);
|
||||
|
||||
for(unsigned int c=0; c < VV.size(); ++c)
|
||||
{
|
||||
double u = VV[c].X();
|
||||
double v = VV[c].Y();
|
||||
double n = VV[c].Z();
|
||||
|
||||
A(c,0) = u*u;
|
||||
A(c,1) = u*v;
|
||||
A(c,2) = v*v;
|
||||
A(c,3) = u;
|
||||
A(c,4) = v;
|
||||
|
||||
b[c] = n;
|
||||
}
|
||||
|
||||
|
||||
static int count = 0, index = 0;
|
||||
double min = 0.000000000001; //1.0e-12
|
||||
/*
|
||||
if (!count)
|
||||
printf("GNE %e\n", min);
|
||||
*/
|
||||
|
||||
if (detCheck && ((A.transpose()*A).determinant() < min && (A.transpose()*A).determinant() > -min))
|
||||
{
|
||||
//A.svd().solve(b, &sol); A.svd().solve(b, &sol);
|
||||
//cout << sol << endl;
|
||||
printf("Quadric: unsolvable vertex %d %d\n", count, ++index);
|
||||
//return Quadric (1, 1, 1, 1, 1);
|
||||
// A.svd().solve(b, &sol);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A);
|
||||
sol=svd.solve(b);
|
||||
return QuadricLocal(sol[0],sol[1],sol[2],sol[3],sol[4]);
|
||||
}
|
||||
count++;
|
||||
|
||||
//for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (svdRes)
|
||||
{
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A);
|
||||
sol=svd.solve(b);
|
||||
//A.svd().solve(b, &sol);
|
||||
}
|
||||
else
|
||||
sol = ((A.transpose()*A).inverse()*A.transpose())*b;
|
||||
|
||||
}
|
||||
|
||||
return QuadricLocal(sol[0],sol[1],sol[2],sol[3],sol[4]);
|
||||
}
|
||||
};
|
||||
|
||||
static void expandMaxLocal (MeshType & mesh, VertexType *v, int max, std::vector<VertexType*> *vv)
|
||||
{
|
||||
Nring<MeshType> rw = Nring<MeshType> (v, &mesh);
|
||||
do rw.expand (); while (rw.allV.size() < max+1);
|
||||
if (rw.allV[0] != v)
|
||||
printf ("rw.allV[0] != *v\n");
|
||||
vv->reserve ((size_t)max);
|
||||
for (int i = 1; i < max+1; i++)
|
||||
vv->push_back(rw.allV[i]);
|
||||
|
||||
rw.clear();
|
||||
}
|
||||
|
||||
|
||||
static void expandSphereLocal (MeshType & mesh, VertexType *v, float r, int min, std::vector<VertexType*> *vv)
|
||||
{
|
||||
Nring<MeshType> rw = Nring<MeshType> (v, &mesh);
|
||||
|
||||
bool isInside = true;
|
||||
while (isInside)
|
||||
{
|
||||
rw.expand();
|
||||
vv->reserve(rw.allV.size());
|
||||
|
||||
typename std::vector<VertexType*>::iterator b = rw.lastV.begin();
|
||||
typename std::vector<VertexType*>::iterator e = rw.lastV.end();
|
||||
isInside = false;
|
||||
while(b != e)
|
||||
{
|
||||
if (((*b)->P() - v->P()).Norm() < r)
|
||||
{
|
||||
vv->push_back(*b);;
|
||||
isInside = true;
|
||||
}
|
||||
++b;
|
||||
}
|
||||
}
|
||||
//printf ("%d\n", vv->size());
|
||||
rw.clear();
|
||||
|
||||
if (vv->size() < min)
|
||||
{
|
||||
vv->clear();
|
||||
expandMaxLocal (mesh, v, min, vv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void getAverageNormal (VertexType *vp, std::vector<VertexType*> &vv, CoordType *ppn)
|
||||
{
|
||||
*ppn = CoordType (0,0,0);
|
||||
for (typename std::vector<VertexType*>::iterator vpi = vv.begin(); vpi != vv.end(); ++vpi)
|
||||
*ppn += (*vpi)->N();
|
||||
*ppn += (*vp).N();
|
||||
*ppn /= vv.size() + 1;
|
||||
ppn->Normalize();
|
||||
}
|
||||
|
||||
|
||||
static void applyProjOnPlane (CoordType ppn, std::vector<VertexType*> &vin, std::vector<VertexType*> *vout)
|
||||
{
|
||||
for (typename std::vector<VertexType*>::iterator vpi = vin.begin(); vpi != vin.end(); ++vpi)
|
||||
if ((*vpi)->N() * ppn > 0.0f)
|
||||
vout->push_back (*vpi);
|
||||
}
|
||||
|
||||
static CoordType projectLocal(VertexType* v, VertexType* vp, CoordType ppn)
|
||||
{
|
||||
return vp->P() - (ppn * ((vp->P() - v->P()) * ppn));
|
||||
}
|
||||
|
||||
|
||||
static void computeReferenceFramesLocal (VertexType *v, CoordType ppn, std::vector<CoordType> *ref)
|
||||
{
|
||||
vcg::face::VFIterator<FaceType> vfi (v);
|
||||
|
||||
int i = (vfi.I() + 1) % 3;
|
||||
VertexTypeP vp = vfi.F()->V(i);
|
||||
|
||||
CoordType x = (projectLocal (v, vp, ppn) - v->P()).Normalize();
|
||||
|
||||
assert(fabs(x * ppn) < 0.1);
|
||||
|
||||
*ref = std::vector<CoordType>(3);
|
||||
|
||||
(*ref)[0] = x;
|
||||
(*ref)[1] = (ppn ^ (*ref)[0]).Normalize();
|
||||
(*ref)[2] = ppn.Normalize(); //ppn / ppn.Norm();
|
||||
}
|
||||
|
||||
|
||||
static void fitQuadricLocal (VertexType *v, std::vector<CoordType> ref, std::vector<VertexType*> &vv, QuadricLocal *q)
|
||||
{
|
||||
bool svdResolution = false;
|
||||
bool zeroDeterminantCheck = false;
|
||||
|
||||
std::vector<CoordType> points;
|
||||
points.reserve (vv.size());
|
||||
|
||||
typename std::vector<VertexType*>::iterator b = vv.begin();
|
||||
typename std::vector<VertexType*>::iterator e = vv.end();
|
||||
|
||||
while(b != e)
|
||||
{
|
||||
CoordType cp = (*b)->P();
|
||||
|
||||
// vtang non e` il v tangente!!!
|
||||
CoordType vTang = cp - v->P();
|
||||
|
||||
double x = vTang * ref[0];
|
||||
double y = vTang * ref[1];
|
||||
double z = vTang * ref[2];
|
||||
points.push_back(CoordType(x,y,z));
|
||||
++b;
|
||||
}
|
||||
|
||||
*q = QuadricLocal::fit (points, svdResolution, zeroDeterminantCheck);
|
||||
}
|
||||
|
||||
|
||||
static void finalEigenStuff (VertexType *v, std::vector<CoordType> ref, QuadricLocal q)
|
||||
{
|
||||
double a = q.a();
|
||||
double b = q.b();
|
||||
double c = q.c();
|
||||
double d = q.d();
|
||||
double e = q.e();
|
||||
|
||||
double E = 1.0 + d*d;
|
||||
double F = d*e;
|
||||
double G = 1.0 + e*e;
|
||||
|
||||
CoordType n = CoordType(-d,-e,1.0).Normalize();
|
||||
|
||||
v->N() = ref[0] * n[0] + ref[1] * n[1] + ref[2] * n[2];
|
||||
|
||||
double L = 2.0 * a * n.Z();
|
||||
double M = b * n.Z();
|
||||
double N = 2 * c * n.Z();
|
||||
|
||||
// ----------------- Eigen stuff
|
||||
Eigen::Matrix2d m;
|
||||
m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F;
|
||||
m = m / (E*G-F*F);
|
||||
Eigen::SelfAdjointEigenSolver<Eigen::Matrix2d> eig(m);
|
||||
|
||||
Eigen::Vector2d c_val = eig.eigenvalues();
|
||||
Eigen::Matrix2d c_vec = eig.eigenvectors();
|
||||
|
||||
c_val = -c_val;
|
||||
|
||||
CoordType v1, v2;
|
||||
v1[0] = c_vec(0);
|
||||
v1[1] = c_vec(1);
|
||||
v1[2] = d * v1[0] + e * v1[1];
|
||||
|
||||
v2[0] = c_vec(2);
|
||||
v2[1] = c_vec(3);
|
||||
v2[2] = d * v2[0] + e * v2[1];
|
||||
|
||||
v1 = v1.Normalize();
|
||||
v2 = v2.Normalize();
|
||||
|
||||
CoordType v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2];
|
||||
CoordType v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2];
|
||||
|
||||
v1global.Normalize();
|
||||
v2global.Normalize();
|
||||
|
||||
v1global *= c_val[0];
|
||||
v2global *= c_val[1];
|
||||
|
||||
if (c_val[0] > c_val[1])
|
||||
{
|
||||
(*v).PD1() = v1global;
|
||||
(*v).PD2() = v2global;
|
||||
(*v).K1() = c_val[0];
|
||||
(*v).K2() = c_val[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
(*v).PD1() = v2global;
|
||||
(*v).PD2() = v1global;
|
||||
(*v).K1() = c_val[1];
|
||||
(*v).K2() = c_val[0];
|
||||
}
|
||||
// ---- end Eigen stuff
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void updateCurvatureLocal (MeshType & mesh, float radiusSphere, vcg::CallBackPos * cb = NULL)
|
||||
{
|
||||
vcg::tri::UpdateFlags<MeshType>::FaceClear(mesh, FaceType::VISITED);
|
||||
bool projectionPlaneCheck = true;
|
||||
int vertexesPerFit = 0;
|
||||
|
||||
int i = 0;
|
||||
VertexIterator vi;
|
||||
for(vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi, i++)
|
||||
{
|
||||
std::vector<VertexType*> vv;
|
||||
std::vector<VertexType*> vvtmp;
|
||||
if (cb && ((i%1024)==00)) {
|
||||
(*cb)(int(100.0f * (float)i / (float)mesh.vn),"Vertices Analysis");
|
||||
}
|
||||
expandSphereLocal (mesh, &*vi, radiusSphere, 5, &vv);
|
||||
|
||||
assert (vv.size() >= 5);
|
||||
|
||||
CoordType ppn;
|
||||
getAverageNormal (&*vi, vv, &ppn);
|
||||
|
||||
if (projectionPlaneCheck)
|
||||
{
|
||||
vvtmp.reserve (vv.size ());
|
||||
applyProjOnPlane (ppn, vv, &vvtmp);
|
||||
if (vvtmp.size() >= 5)
|
||||
vv = vvtmp;
|
||||
}
|
||||
|
||||
vvtmp.clear();
|
||||
assert (vv.size() >= 5);
|
||||
|
||||
std::vector<CoordType> ref;
|
||||
computeReferenceFramesLocal (&*vi, ppn, &ref);
|
||||
|
||||
vertexesPerFit += vv.size();
|
||||
|
||||
QuadricLocal q;
|
||||
fitQuadricLocal (&*vi, ref, vv, &q);
|
||||
|
||||
finalEigenStuff (&*vi, ref, q);
|
||||
|
||||
}
|
||||
|
||||
if (cb)
|
||||
printf ("average vertex num in each fit: %f\n", ((float) vertexesPerFit) / mesh.vn);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,484 @@
|
||||
/****************************************************************************
|
||||
* 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 FITMAPS_H
|
||||
#define FITMAPS_H
|
||||
|
||||
#include <vcg/math/histogram.h>
|
||||
|
||||
#include <vcg/simplex/face/jumping_pos.h>
|
||||
#include <vcg/complex/algorithms/update/normal.h>
|
||||
#include <vcg/complex/algorithms/update/curvature.h>
|
||||
#include <vcg/complex/algorithms/update/topology.h>
|
||||
#include <vcg/complex/algorithms/update/bounding.h>
|
||||
#include "vcg/complex/algorithms/update/curvature_fitting.h"
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/QR>
|
||||
#include <Eigen/LU>
|
||||
#include <Eigen/SVD>
|
||||
|
||||
#include <vcg/complex/algorithms/nring.h>
|
||||
|
||||
#include <vcg/complex/algorithms/smooth.h>
|
||||
|
||||
namespace vcg { namespace tri {
|
||||
|
||||
template<class MeshType>
|
||||
class Fitmaps
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::CoordType CoordType;
|
||||
|
||||
typedef vcg::tri::Nring<MeshType> RingWalker;
|
||||
|
||||
|
||||
class Bicubic
|
||||
{
|
||||
public:
|
||||
|
||||
Bicubic() {};
|
||||
|
||||
Bicubic(vector<double>& input)
|
||||
{
|
||||
data = input;
|
||||
if (input.size() != 16)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// (u3 u2 u 1) (v3 v2 v 1)
|
||||
//
|
||||
// a u3 v3
|
||||
// b u3 v2
|
||||
// c u3 v1
|
||||
// d u3 1
|
||||
// e u2 v3
|
||||
// f u2 v2
|
||||
// g u2 v1
|
||||
// h u2 1
|
||||
// i u1 v3
|
||||
// l u1 v2
|
||||
// m u1 v1
|
||||
// n u1 1
|
||||
// o 1 v3
|
||||
// p 1 v2
|
||||
// q 1 v1
|
||||
// r 1 1
|
||||
|
||||
double& a() { return data[0];}
|
||||
double& b() { return data[1];}
|
||||
double& c() { return data[2];}
|
||||
double& d() { return data[3];}
|
||||
double& e() { return data[4];}
|
||||
double& f() { return data[5];}
|
||||
double& g() { return data[6];}
|
||||
double& h() { return data[7];}
|
||||
double& i() { return data[8];}
|
||||
double& l() { return data[9];}
|
||||
double& m() { return data[10];}
|
||||
double& n() { return data[11];}
|
||||
double& o() { return data[12];}
|
||||
double& p() { return data[13];}
|
||||
double& q() { return data[14];}
|
||||
double& r() { return data[15];}
|
||||
|
||||
vector<double> data;
|
||||
|
||||
double evaluate(double u, double v)
|
||||
{
|
||||
|
||||
return
|
||||
a() * u*u*u * v*v*v +
|
||||
b() * u*u*u * v*v +
|
||||
c() * u*u*u * v +
|
||||
d() * u*u*u +
|
||||
e() * u*u * v*v*v +
|
||||
f() * u*u * v*v +
|
||||
g() * u*u * v +
|
||||
h() * u*u +
|
||||
i() * u * v*v*v +
|
||||
l() * u * v*v +
|
||||
m() * u * v +
|
||||
n() * u +
|
||||
o() * v*v*v +
|
||||
p() * v*v +
|
||||
q() * v +
|
||||
r();
|
||||
}
|
||||
|
||||
|
||||
double distanceRMS(std::vector<CoordType>& VV)
|
||||
{
|
||||
double error = 0;
|
||||
for(typename std::vector<CoordType>::iterator it = VV.begin(); it != VV.end(); ++it)
|
||||
{
|
||||
double u = it->X();
|
||||
double v = it->Y();
|
||||
double n = it->Z();
|
||||
|
||||
double temp = evaluate(u,v);
|
||||
error += (n - temp)*(n - temp);
|
||||
}
|
||||
error /= (double) VV.size();
|
||||
return sqrt(error);
|
||||
}
|
||||
|
||||
static Bicubic fit(std::vector<CoordType> VV)
|
||||
{
|
||||
assert(VV.size() >= 16);
|
||||
Eigen::MatrixXf A(VV.size(),16);
|
||||
Eigen::MatrixXf b(VV.size(),1);
|
||||
Eigen::MatrixXf sol(16,1);
|
||||
|
||||
for(unsigned int c=0; c < VV.size(); ++c)
|
||||
{
|
||||
double u = VV[c].X();
|
||||
double v = VV[c].Y();
|
||||
double n = VV[c].Z();
|
||||
|
||||
A(c,0) = u*u*u * v*v*v;
|
||||
A(c,1) = u*u*u * v*v;
|
||||
A(c,2) = u*u*u * v;
|
||||
A(c,3) = u*u*u;
|
||||
A(c,4) = u*u * v*v*v;
|
||||
A(c,5) = u*u * v*v;
|
||||
A(c,6) = u*u * v;
|
||||
A(c,7) = u*u;
|
||||
A(c,8) = u * v*v*v;
|
||||
A(c,9) = u * v*v;
|
||||
A(c,10) = u * v;
|
||||
A(c,11) = u;
|
||||
A(c,12) = v*v*v;
|
||||
A(c,13) = v*v;
|
||||
A(c,14) = v;
|
||||
A(c,15) = 1;
|
||||
|
||||
|
||||
b[c] = n;
|
||||
}
|
||||
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A);
|
||||
sol=svd.solve(b);
|
||||
// A.svd().solve(b, &sol);
|
||||
|
||||
vector<double> r(16);
|
||||
|
||||
for (int i=0; i < 16; ++i)
|
||||
r.at(i) = sol[i];
|
||||
|
||||
return Bicubic(r);
|
||||
}
|
||||
};
|
||||
|
||||
Fitmaps()
|
||||
{}
|
||||
|
||||
class radSorter
|
||||
{
|
||||
public:
|
||||
|
||||
radSorter(VertexType* v)
|
||||
{
|
||||
origin = v;
|
||||
}
|
||||
|
||||
VertexType* origin;
|
||||
|
||||
bool operator() (VertexType* v1, VertexType* v2)
|
||||
{
|
||||
return (v1->P() - origin->P()).SquaredNorm() < (v2->P() - origin->P()).SquaredNorm();
|
||||
}
|
||||
};
|
||||
|
||||
float getMeanCurvature(VertexType* vp)
|
||||
{
|
||||
return (vp->K1() + vp->K2())/2.0;
|
||||
}
|
||||
|
||||
static bool fitBicubicPoints(VertexType* v, std::vector<CoordType>& ref, Bicubic& ret, std::vector<CoordType>& points, std::vector<VertexType*>& ring)
|
||||
{
|
||||
points.clear();
|
||||
|
||||
if (ring.size() < 16)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
typename std::vector<VertexType*>::iterator b = ring.begin();
|
||||
typename std::vector<VertexType*>::iterator e = ring.end();
|
||||
|
||||
while(b != e)
|
||||
{
|
||||
CoordType vT = (*b)->P() - v->P();
|
||||
|
||||
double x = vT * ref[0];
|
||||
double y = vT * ref[1];
|
||||
double z = vT * ref[2];
|
||||
|
||||
points.push_back(CoordType(x,y,z));
|
||||
++b;
|
||||
}
|
||||
ret = Bicubic::fit(points);
|
||||
return true;
|
||||
}
|
||||
|
||||
static double AverageEdgeLenght(MeshType& m)
|
||||
{
|
||||
double doubleA = 0;
|
||||
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
||||
doubleA+=vcg::DoubleArea(*fi);
|
||||
}
|
||||
int nquads = m.fn / 2;
|
||||
return sqrt( doubleA/(2*nquads) );
|
||||
}
|
||||
|
||||
static void computeMFitmap(MeshType& m, float perc, int ringMax = 50)
|
||||
{
|
||||
vcg::tri::UpdateCurvatureFitting<MeshType>::computeCurvature(m);
|
||||
vcg::tri::UpdateNormal<MeshType>::PerVertexAngleWeighted(m);
|
||||
|
||||
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
|
||||
vcg::tri::UpdateTopology<MeshType>::VertexFace(m);
|
||||
|
||||
vcg::tri::UpdateBounding<MeshType>::Box(m);
|
||||
|
||||
int countTemp = 0;
|
||||
|
||||
RingWalker::clearFlags(&m);
|
||||
|
||||
for(VertexIterator it=m.vert.begin(); it!=m.vert.end();++it)
|
||||
{
|
||||
if ((countTemp++ % 100) == 0)
|
||||
cerr << countTemp << "/" << m.vert.size() << endl;
|
||||
|
||||
RingWalker rw(&*it,&m);
|
||||
|
||||
CoordType nor = it->N();
|
||||
|
||||
float okfaces = 0;
|
||||
float flipfaces = 0;
|
||||
|
||||
int count = 0;
|
||||
do
|
||||
{
|
||||
count++;
|
||||
rw.expand();
|
||||
for(unsigned i=0; i<rw.lastF.size();++i)
|
||||
{
|
||||
CoordType vet1 = nor;
|
||||
CoordType vet2 = rw.lastF[i]->N();
|
||||
|
||||
vet1.Normalize();
|
||||
vet2.Normalize();
|
||||
|
||||
|
||||
double scal = vet1 * vet2;
|
||||
if ((scal) > 0)
|
||||
okfaces += (vcg::DoubleArea(*rw.lastF[i]));
|
||||
else
|
||||
flipfaces += (vcg::DoubleArea(*rw.lastF[i]));
|
||||
}
|
||||
} while ((((flipfaces)/(okfaces + flipfaces))*100.0 < perc) && (count < ringMax));
|
||||
|
||||
std::sort(rw.lastV.begin(),rw.lastV.end(),radSorter(&*it));
|
||||
|
||||
it->Q() = ((*rw.lastV.begin())->P() - it->P()).Norm();
|
||||
rw.clear();
|
||||
|
||||
}
|
||||
|
||||
vcg::tri::Smooth<MeshType>::VertexQualityLaplacian(m,2);
|
||||
}
|
||||
|
||||
static vector<VertexType*> gatherNeighsSurface(VertexType* vt, float sigma, MeshType& m)
|
||||
{
|
||||
vector<VertexType*> current;
|
||||
|
||||
RingWalker rw(vt,&m);
|
||||
|
||||
bool exit = false;
|
||||
|
||||
do
|
||||
{
|
||||
rw.expand();
|
||||
|
||||
exit = true;
|
||||
|
||||
for(typename vector<VertexType*>::iterator it = rw.lastV.begin(); it != rw.lastV.end(); ++it)
|
||||
{
|
||||
if (((*it)->P() - vt->P()).Norm() < sigma)
|
||||
{
|
||||
current.push_back(*it);
|
||||
exit = false;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!exit);
|
||||
|
||||
rw.clear();
|
||||
return current;
|
||||
}
|
||||
|
||||
static void computeSFitmap(MeshType& m)//, float target = 1000)
|
||||
{
|
||||
|
||||
vcg::tri::UpdateCurvatureFitting<MeshType>::computeCurvature(m);
|
||||
vcg::tri::UpdateNormal<MeshType>::PerVertexAngleWeighted(m);
|
||||
|
||||
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
|
||||
vcg::tri::UpdateTopology<MeshType>::VertexFace(m);
|
||||
|
||||
// update bounding box
|
||||
vcg::tri::UpdateBounding<MeshType>::Box(m);
|
||||
|
||||
int countTemp = 0;
|
||||
|
||||
double e = AverageEdgeLenght(m);
|
||||
|
||||
int iteraz = 5; //2.0 * sqrt(m.vert.size()/target);
|
||||
|
||||
for(VertexIterator it=m.vert.begin(); it!=m.vert.end();++it)
|
||||
{
|
||||
if ((countTemp++ % 100) == 0)
|
||||
cerr << countTemp << "/" << m.vert.size() << endl;
|
||||
|
||||
vector<float> oneX;
|
||||
|
||||
|
||||
for (int iteration = 0; iteration<iteraz; ++iteration)
|
||||
{
|
||||
oneX.push_back((iteration+1)*(e));
|
||||
}
|
||||
|
||||
std::vector<CoordType> ref(3);
|
||||
ref[0] = it->PD1();
|
||||
ref[1] = it->PD2();
|
||||
ref[2] = it->PD1() ^ it->PD2();
|
||||
|
||||
ref[0].Normalize();
|
||||
ref[1].Normalize();
|
||||
ref[2].Normalize();
|
||||
|
||||
Bicubic b;
|
||||
|
||||
RingWalker::clearFlags(&m);
|
||||
|
||||
std::vector<VertexType*> pointsGlobal = gatherNeighsSurface(&*it,oneX.at(iteraz-1),m);
|
||||
|
||||
vector<float> onedimensional;
|
||||
|
||||
for (int iteration = 0; iteration<iteraz; ++iteration)
|
||||
{
|
||||
std::vector<VertexType*> points; // solo quelli nel raggio
|
||||
|
||||
std::vector<CoordType> projected; // come sopra ma in coord locali
|
||||
for (typename std::vector<VertexType*>::iterator it2 = pointsGlobal.begin(); it2 != pointsGlobal.end(); ++it2)
|
||||
{
|
||||
if (((*it).P() - (*it2)->P()).Norm() < oneX.at(iteration))
|
||||
points.push_back(*it2);
|
||||
}
|
||||
|
||||
std::vector<VertexType*>& pointsFitting = points;
|
||||
|
||||
|
||||
if (!fitBicubicPoints(&*it, ref, b, projected,pointsFitting))
|
||||
{
|
||||
onedimensional.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
onedimensional.push_back(b.distanceRMS(projected));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// // vecchio fit ax^4
|
||||
Eigen::MatrixXf Am(onedimensional.size(),1);
|
||||
Eigen::MatrixXf bm(onedimensional.size(),1);
|
||||
Eigen::MatrixXf sol(1,1);
|
||||
|
||||
for(unsigned int c=0; c < onedimensional.size(); ++c)
|
||||
{
|
||||
double x = oneX.at(c);
|
||||
|
||||
Am(c,0) = pow(x,4);
|
||||
bm[c] = onedimensional[c];
|
||||
}
|
||||
|
||||
|
||||
// Am.svd().solve(bm, &sol);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(Am);
|
||||
sol=svd.solve(bm);
|
||||
|
||||
it->Q() = pow((double)sol[0],0.25);
|
||||
|
||||
// // nuovo fit ax^4 + b
|
||||
// Eigen::MatrixXf Am(onedimensional.size()+1,2);
|
||||
// Eigen::MatrixXf bm(onedimensional.size()+1,1);
|
||||
// Eigen::MatrixXf sol(2,1);
|
||||
//
|
||||
// Am(0,0) = 0;
|
||||
// Am(0,1) = 0;
|
||||
// bm[0] = 0;
|
||||
//
|
||||
// for(unsigned int c=0; c < onedimensional.size(); ++c)
|
||||
// {
|
||||
// double x = oneX.at(c);
|
||||
//
|
||||
// Am(c,0) = pow(x,4);
|
||||
// Am(c,1) = 1;
|
||||
// bm[c] = onedimensional[c];
|
||||
// }
|
||||
//
|
||||
// //sol = ((Am.transpose()*Am).inverse()*Am.transpose())*bm;
|
||||
// Am.svd().solve(bm, &sol);
|
||||
//
|
||||
// cerr << "------" << sol[0] << " " << sol[1] << endl;
|
||||
// if (sol[0] > 0)
|
||||
// saliency[it] = pow((double)sol[0],0.25);
|
||||
// else
|
||||
// saliency[it] = 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
vcg::tri::Smooth<MeshType>::VertexQualityLaplacian(m,1);
|
||||
}
|
||||
|
||||
|
||||
~Fitmaps(){};
|
||||
|
||||
};
|
||||
|
||||
}} // END NAMESPACES
|
||||
|
||||
#endif // FITMAPS_H
|
||||
@@ -0,0 +1,533 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_FLAGS
|
||||
#define __VCG_TRI_UPDATE_FLAGS
|
||||
|
||||
#include <vcg/simplex/face/pos.h>
|
||||
#include <vcg/simplex/tetrahedron/pos.h>
|
||||
#include <vcg/simplex/edge/pos.h>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile flag.h vcg/complex/algorithms/update/flag.h
|
||||
|
||||
/// \brief Management, updating and computation of per-vertex and per-face flags (like border flags).
|
||||
|
||||
/**
|
||||
This class is used to compute or update some of the flags that can be stored in the mesh components. For now just Border flags (e.g. the flag that tells if a given edge of a face belong to a border of the mesh or not).
|
||||
*/
|
||||
|
||||
template <class UpdateMeshType>
|
||||
class UpdateFlags
|
||||
{
|
||||
|
||||
public:
|
||||
typedef UpdateMeshType MeshType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::EdgeType EdgeType;
|
||||
typedef typename MeshType::EdgePointer EdgePointer;
|
||||
typedef typename MeshType::EdgeIterator EdgeIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::TetraType TetraType;
|
||||
typedef typename MeshType::TetraPointer TetraPointer;
|
||||
typedef typename MeshType::TetraIterator TetraIterator;
|
||||
|
||||
/// \brief Reset all the mesh flags (vertexes edge faces) setting everithing to zero (the default value for flags)
|
||||
|
||||
static void Clear(MeshType &m)
|
||||
{
|
||||
if(HasPerVertexFlags(m) )
|
||||
for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi)
|
||||
(*vi).Flags() = 0;
|
||||
if(HasPerEdgeFlags(m) )
|
||||
for(EdgeIterator ei=m.edge.begin(); ei!=m.edge.end(); ++ei)
|
||||
(*ei).Flags() = 0;
|
||||
if(HasPerFaceFlags(m) )
|
||||
for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi)
|
||||
(*fi).Flags() = 0;
|
||||
if(HasPerTetraFlags(m) )
|
||||
for(TetraIterator ti=m.tetra.begin(); ti!=m.tetra.end(); ++ti)
|
||||
(*ti).Flags() = 0;
|
||||
}
|
||||
|
||||
|
||||
static void VertexClear(MeshType &m, unsigned int FlagMask = 0xffffffff)
|
||||
{
|
||||
RequirePerVertexFlags(m);
|
||||
int andMask = ~FlagMask;
|
||||
for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi)
|
||||
if(!(*vi).IsD()) (*vi).Flags() &= andMask ;
|
||||
}
|
||||
|
||||
static void EdgeClear(MeshType &m, unsigned int FlagMask = 0xffffffff)
|
||||
{
|
||||
RequirePerEdgeFlags(m);
|
||||
int andMask = ~FlagMask;
|
||||
for(EdgeIterator ei=m.edge.begin(); ei!=m.edge.end(); ++ei)
|
||||
if(!(*ei).IsD()) (*ei).Flags() &= andMask ;
|
||||
}
|
||||
|
||||
static void FaceClear(MeshType &m, unsigned int FlagMask = 0xffffffff)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
int andMask = ~FlagMask;
|
||||
for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi)
|
||||
if(!(*fi).IsD()) (*fi).Flags() &= andMask ;
|
||||
}
|
||||
|
||||
static void TetraClear(MeshType &m, unsigned int FlagMask = 0xffffffff)
|
||||
{
|
||||
RequirePerTetraFlags(m);
|
||||
int andMask = ~FlagMask;
|
||||
for(TetraIterator ti=m.tetra.begin(); ti!=m.tetra.end(); ++ti)
|
||||
if(!(*ti).IsD()) (*ti).Flags() &= andMask ;
|
||||
}
|
||||
|
||||
static void VertexSet(MeshType &m, unsigned int FlagMask)
|
||||
{
|
||||
RequirePerVertexFlags(m);
|
||||
for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi)
|
||||
if(!(*vi).IsD()) (*vi).Flags() |= FlagMask ;
|
||||
}
|
||||
|
||||
static void EdgeSet(MeshType &m, unsigned int FlagMask)
|
||||
{
|
||||
RequirePerEdgeFlags(m);
|
||||
for(EdgeIterator ei=m.edge.begin(); ei!=m.edge.end(); ++ei)
|
||||
if(!(*ei).IsD()) (*ei).Flags() |= FlagMask ;
|
||||
}
|
||||
|
||||
static void FaceSet(MeshType &m, unsigned int FlagMask)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi)
|
||||
if(!(*fi).IsD()) (*fi).Flags() |= FlagMask ;
|
||||
}
|
||||
|
||||
static void TetraSet(MeshType &m, unsigned int FlagMask)
|
||||
{
|
||||
RequirePerTetraFlags(m);
|
||||
for(TetraIterator ti=m.tetra.begin(); ti!=m.tetra.end(); ++ti)
|
||||
if(!(*ti).IsD()) (*ti).Flags() |= FlagMask ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void VertexClearV(MeshType &m) { VertexClear(m,VertexType::VISITED);}
|
||||
static void VertexClearS(MeshType &m) { VertexClear(m,VertexType::SELECTED);}
|
||||
static void VertexClearB(MeshType &m) { VertexClear(m,VertexType::BORDER);}
|
||||
static void EdgeClearV(MeshType &m) { EdgeClear(m,EdgeType::VISITED);}
|
||||
static void FaceClearV(MeshType &m) { FaceClear(m,FaceType::VISITED);}
|
||||
static void FaceClearB(MeshType &m) { FaceClear(m,FaceType::BORDER012);}
|
||||
static void FaceClearS(MeshType &m) {FaceClear(m,FaceType::SELECTED);}
|
||||
static void FaceClearF(MeshType &m) { FaceClear(m,FaceType::FAUX012);}
|
||||
static void FaceClearFaceEdgeS(MeshType &m) { FaceClear(m,FaceType::FACEEDGESEL012 ); }
|
||||
|
||||
static void EdgeSetV(MeshType &m) { EdgeSet(m,EdgeType::VISITED);}
|
||||
static void VertexSetV(MeshType &m) { VertexSet(m,VertexType::VISITED);}
|
||||
static void VertexSetS(MeshType &m) { VertexSet(m,VertexType::SELECTED);}
|
||||
static void VertexSetB(MeshType &m) { VertexSet(m,VertexType::BORDER);}
|
||||
static void FaceSetV(MeshType &m) { FaceSet(m,FaceType::VISITED);}
|
||||
static void FaceSetB(MeshType &m) { FaceSet(m,FaceType::BORDER);}
|
||||
static void FaceSetF(MeshType &m) { FaceSet(m,FaceType::FAUX012);}
|
||||
static void TetraClearV(MeshType &m) { TetraClear(m, TetraType::VISITED); }
|
||||
static void TetraClearS(MeshType &m) { TetraClear(m, TetraType::SELECTED); }
|
||||
static void TetraClearB(MeshType &m) { TetraClear(m, TetraType::BORDER0123); }
|
||||
static void TetraSetV(MeshType &m) { TetraSet(m, TetraType::VISITED); }
|
||||
static void TetraSetS(MeshType &m) { TetraSet(m, TetraType::SELECTED); }
|
||||
static void TetraSetB(MeshType &m) { TetraSet(m, TetraType::BORDER0123); }
|
||||
/// \brief Compute the border flags for the faces using the Face-Face Topology.
|
||||
/**
|
||||
\warning Obviously it assumes that the topology has been correctly computed (see: UpdateTopology::FaceFace )
|
||||
*/
|
||||
static void FaceBorderFromFF(MeshType &m)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
RequireFFAdjacency(m);
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD())
|
||||
for(int j=0;j<fi->VN();++j)
|
||||
{
|
||||
if(face::IsBorder(*fi,j)) (*fi).SetB(j);
|
||||
else (*fi).ClearB(j);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Compute the border flags for the tetras using the Tetra-Tetra Topology.
|
||||
/**
|
||||
\warning Obviously it assumes that the topology has been correctly computed (see: UpdateTopology::FaceFace )
|
||||
*/
|
||||
static void TetraBorderFromTT(MeshType &m)
|
||||
{
|
||||
RequirePerTetraFlags(m);
|
||||
RequireTTAdjacency(m);
|
||||
|
||||
for(TetraIterator ti=m.tetra.begin(); ti!=m.tetra.end(); ++ti)
|
||||
if(!(*ti).IsD())
|
||||
for(int j = 0; j < 4; ++j)
|
||||
{
|
||||
if (IsTTBorder(*ti,j)) (*ti).SetB(j);
|
||||
else (*ti).ClearB(j);
|
||||
}
|
||||
}
|
||||
|
||||
static void VertexBorderFromTT(MeshType &m)
|
||||
{
|
||||
RequirePerVertexFlags(m);
|
||||
RequireTTAdjacency(m);
|
||||
|
||||
VertexClearB(m);
|
||||
|
||||
for(TetraIterator ti=m.tetra.begin(); ti!=m.tetra.end(); ++ti)
|
||||
if(!(*ti).IsD())
|
||||
for(int j = 0; j < 4; ++j)
|
||||
{
|
||||
if (IsTTBorder(*ti,j))
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
ti->V(Tetra::VofF(j, i))->SetB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void FaceBorderFromVF(MeshType &m)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
RequireVFAdjacency(m);
|
||||
|
||||
FaceClearB(m);
|
||||
int visitedBit=VertexType::NewBitFlag();
|
||||
|
||||
// Calcolo dei bordi
|
||||
// per ogni vertice vi si cercano i vertici adiacenti che sono toccati da una faccia sola
|
||||
// (o meglio da un numero dispari di facce)
|
||||
|
||||
const int BORDERFLAG[3]={FaceType::BORDER0, FaceType::BORDER1, FaceType::BORDER2};
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD())
|
||||
{
|
||||
for(face::VFIterator<FaceType> vfi(&*vi) ; !vfi.End(); ++vfi )
|
||||
{
|
||||
vfi.f->V1(vfi.z)->ClearUserBit(visitedBit);
|
||||
vfi.f->V2(vfi.z)->ClearUserBit(visitedBit);
|
||||
}
|
||||
for(face::VFIterator<FaceType> vfi(&*vi) ; !vfi.End(); ++vfi )
|
||||
{
|
||||
if(vfi.f->V1(vfi.z)->IsUserBit(visitedBit)) vfi.f->V1(vfi.z)->ClearUserBit(visitedBit);
|
||||
else vfi.f->V1(vfi.z)->SetUserBit(visitedBit);
|
||||
if(vfi.f->V2(vfi.z)->IsUserBit(visitedBit)) vfi.f->V2(vfi.z)->ClearUserBit(visitedBit);
|
||||
else vfi.f->V2(vfi.z)->SetUserBit(visitedBit);
|
||||
}
|
||||
for(face::VFIterator<FaceType> vfi(&*vi) ; !vfi.End(); ++vfi )
|
||||
{
|
||||
if(vfi.f->V(vfi.z)< vfi.f->V1(vfi.z) && vfi.f->V1(vfi.z)->IsUserBit(visitedBit))
|
||||
vfi.f->Flags() |= BORDERFLAG[vfi.z];
|
||||
if(vfi.f->V(vfi.z)< vfi.f->V2(vfi.z) && vfi.f->V2(vfi.z)->IsUserBit(visitedBit))
|
||||
vfi.f->Flags() |= BORDERFLAG[(vfi.z+2)%3];
|
||||
}
|
||||
}
|
||||
VertexType::DeleteBitFlag(visitedBit);
|
||||
}
|
||||
|
||||
|
||||
class EdgeSorter
|
||||
{
|
||||
public:
|
||||
|
||||
VertexPointer v[2]; // Puntatore ai due vertici (Ordinati)
|
||||
FacePointer f; // Puntatore alla faccia generatrice
|
||||
int z; // Indice dell'edge nella faccia
|
||||
|
||||
EdgeSorter() {} // Nothing to do
|
||||
|
||||
|
||||
void Set( const FacePointer pf, const int nz )
|
||||
{
|
||||
assert(pf!=0);
|
||||
assert(nz>=0);
|
||||
assert(nz<3);
|
||||
|
||||
v[0] = pf->V(nz);
|
||||
v[1] = pf->V((nz+1)%3);
|
||||
assert(v[0] != v[1]);
|
||||
|
||||
if( v[0] > v[1] ) std::swap(v[0],v[1]);
|
||||
f = pf;
|
||||
z = nz;
|
||||
}
|
||||
|
||||
inline bool operator < ( const EdgeSorter & pe ) const {
|
||||
if( v[0]<pe.v[0] ) return true;
|
||||
else if( v[0]>pe.v[0] ) return false;
|
||||
else return v[1] < pe.v[1];
|
||||
}
|
||||
|
||||
inline bool operator == ( const EdgeSorter & pe ) const
|
||||
{
|
||||
return v[0]==pe.v[0] && v[1]==pe.v[1];
|
||||
}
|
||||
inline bool operator != ( const EdgeSorter & pe ) const
|
||||
{
|
||||
return v[0]!=pe.v[0] || v[1]!=pe.v[1];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// versione minimale che non calcola i complex flag.
|
||||
static void VertexBorderFromNone(MeshType &m)
|
||||
{
|
||||
RequirePerVertexFlags(m);
|
||||
|
||||
std::vector<EdgeSorter> e;
|
||||
typename UpdateMeshType::FaceIterator pf;
|
||||
typename std::vector<EdgeSorter>::iterator p;
|
||||
|
||||
if( m.fn == 0 )
|
||||
return;
|
||||
|
||||
e.resize(m.fn*3); // Alloco il vettore ausiliario
|
||||
p = e.begin();
|
||||
for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce
|
||||
if( ! (*pf).IsD() )
|
||||
for(int j=0;j<3;++j)
|
||||
{
|
||||
(*p).Set(&(*pf),j);
|
||||
(*pf).ClearB(j);
|
||||
++p;
|
||||
}
|
||||
assert(p==e.end());
|
||||
sort(e.begin(), e.end()); // Lo ordino per vertici
|
||||
|
||||
typename std::vector<EdgeSorter>::iterator pe,ps;
|
||||
for(ps = e.begin(), pe = e.begin(); pe < e.end(); ++pe) // Scansione vettore ausiliario
|
||||
{
|
||||
if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali
|
||||
{
|
||||
if(pe-ps==1) {
|
||||
ps->v[0]->SetB();
|
||||
ps->v[1]->SetB();
|
||||
}/* else
|
||||
if(pe-ps!=2) { // not twomanyfold!
|
||||
for(;ps!=pe;++ps) {
|
||||
ps->v[0]->SetB(); // Si settano border anche i complex.
|
||||
ps->v[1]->SetB();
|
||||
}
|
||||
}*/
|
||||
ps = pe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes per-face border flags without requiring any kind of topology
|
||||
/// It has a O(fn log fn) complexity.
|
||||
static void FaceBorderFromNone(MeshType &m)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
|
||||
std::vector<EdgeSorter> e;
|
||||
typename UpdateMeshType::FaceIterator pf;
|
||||
typename std::vector<EdgeSorter>::iterator p;
|
||||
|
||||
for(VertexIterator v=m.vert.begin();v!=m.vert.end();++v)
|
||||
(*v).ClearB();
|
||||
|
||||
if( m.fn == 0 )
|
||||
return;
|
||||
|
||||
FaceIterator fi;
|
||||
int n_edges = 0;
|
||||
for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN();
|
||||
e.resize(n_edges);
|
||||
|
||||
p = e.begin();
|
||||
for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce
|
||||
if( ! (*pf).IsD() )
|
||||
for(int j=0;j<(*pf).VN();++j)
|
||||
{
|
||||
(*p).Set(&(*pf),j);
|
||||
(*pf).ClearB(j);
|
||||
++p;
|
||||
}
|
||||
assert(p==e.end());
|
||||
sort(e.begin(), e.end()); // Lo ordino per vertici
|
||||
|
||||
typename std::vector<EdgeSorter>::iterator pe,ps;
|
||||
ps = e.begin();pe=e.begin();
|
||||
do
|
||||
{
|
||||
if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali
|
||||
{
|
||||
if(pe-ps==1) {
|
||||
ps->f->SetB(ps->z);
|
||||
} /*else
|
||||
if(pe-ps!=2) { // Caso complex!!
|
||||
for(;ps!=pe;++ps)
|
||||
ps->f->SetB(ps->z); // Si settano border anche i complex.
|
||||
}*/
|
||||
ps = pe;
|
||||
}
|
||||
if(pe==e.end()) break;
|
||||
++pe;
|
||||
} while(true);
|
||||
// TRACE("found %i border (%i complex) on %i edges\n",nborder,ncomplex,ne);
|
||||
}
|
||||
|
||||
/// Compute the PerVertex Border flag deriving it from the face-face adjacency
|
||||
static void VertexBorderFromFaceAdj(MeshType &m)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
RequirePerVertexFlags(m);
|
||||
RequireFFAdjacency(m);
|
||||
// MeshAssert<MeshType>::FFAdjacencyIsInitialized(m);
|
||||
|
||||
VertexClearB(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
|
||||
for(int z=0;z<(*fi).VN();++z)
|
||||
if( face::IsBorder(*fi,z))
|
||||
{
|
||||
(*fi).V0(z)->SetB();
|
||||
(*fi).V1(z)->SetB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the PerVertex Border flag deriving it from the border flag of faces
|
||||
static void VertexBorderFromFaceBorder(MeshType &m)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
RequirePerVertexFlags(m);
|
||||
VertexClearB(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
for(int z=0;z<(*fi).VN();++z)
|
||||
if( (*fi).IsB(z) )
|
||||
{
|
||||
(*fi).V(z)->SetB();
|
||||
(*fi).V((*fi).Next(z))->SetB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the PerVertex Border flag deriving it from the Edge-Edge adjacency (made for edgemeshes)
|
||||
static void VertexBorderFromEdgeAdj(MeshType &m)
|
||||
{
|
||||
RequirePerVertexFlags(m);
|
||||
RequireEEAdjacency(m);
|
||||
|
||||
VertexClearB(m);
|
||||
for (EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
|
||||
if (!ei->IsD())
|
||||
{
|
||||
for (int z=0; z<2; ++z)
|
||||
if (edge::IsEdgeBorder(*ei, z))
|
||||
{
|
||||
ei->V(z)->SetB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Marks feature edges according to two signed dihedral angles.
|
||||
/// Actually it uses the face_edge selection bit on faces,
|
||||
/// we select the edges where the signed dihedral angle between the normal of two incident faces ,
|
||||
/// is outside the two given thresholds.
|
||||
/// In this way all the edges that are almost planar are marked as non selected (e.g. edges to be ignored)
|
||||
/// Note that it uses the signed dihedral angle convention (negative for concave edges and positive for convex ones);
|
||||
///
|
||||
/// Optionally it can also mark as feature edges also the boundary edges.
|
||||
///
|
||||
static void FaceEdgeSelSignedCrease(MeshType &m, float AngleRadNeg, float AngleRadPos, bool MarkBorderFlag = false )
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
RequireFFAdjacency(m);
|
||||
//initially Nothing is marked
|
||||
FaceClearFaceEdgeS(m);
|
||||
// Then select the edge as crease only if the signed angle is outside the range.
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
{
|
||||
for(int z=0;z<(*fi).VN();++z)
|
||||
{
|
||||
if(!face::IsBorder(*fi,z) )
|
||||
{
|
||||
ScalarType angle = DihedralAngleRad(*fi,z);
|
||||
if(angle<AngleRadNeg || angle>AngleRadPos)
|
||||
(*fi).SetFaceEdgeS(z);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(MarkBorderFlag) (*fi).SetFaceEdgeS(z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Selects feature edges according to Face adjacency.
|
||||
///
|
||||
static void FaceEdgeSelBorder(MeshType &m)
|
||||
{
|
||||
RequirePerFaceFlags(m);
|
||||
RequireFFAdjacency(m);
|
||||
//initially Nothing is selected
|
||||
FaceClearFaceEdgeS(m);
|
||||
for (FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi)
|
||||
if (!fi->IsD())
|
||||
{
|
||||
for (int z=0; z<(*fi).VN(); ++z)
|
||||
{
|
||||
if (face::IsBorder(*fi,z))
|
||||
fi->SetFaceEdgeS(z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Marks feature edges according to a given angle
|
||||
/// Actually it uses the face_edge selection bit on faces,
|
||||
/// we select the edges where the dihedral angle between the normal of two incident faces is larger than ,
|
||||
/// the given thresholds.
|
||||
/// In this way all the near planar edges are marked remains not selected (e.g. edges to be ignored)
|
||||
static void FaceEdgeSelCrease(MeshType &m,float AngleRad)
|
||||
{
|
||||
FaceEdgeSelSignedCrease(m,-AngleRad,AngleRad);
|
||||
}
|
||||
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace tri
|
||||
} // End namespace vcg
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,656 @@
|
||||
/****************************************************************************
|
||||
* 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_HALFEDGE_
|
||||
#define __VCGLIB_HALFEDGE_
|
||||
|
||||
#include <vcg/complex/algorithms/clean.h>
|
||||
#include <vcg/complex/algorithms/update/topology.h>
|
||||
#include <vcg/complex/algorithms/update/halfedge_topology.h>
|
||||
|
||||
namespace vcg
|
||||
{
|
||||
namespace tri{
|
||||
/// \ingroup trimesh
|
||||
/// \brief This class is used to build edge based data structure from indexed data structure and viceversa
|
||||
/**
|
||||
*/
|
||||
|
||||
template <class MeshType >
|
||||
class UpdateHalfEdges{
|
||||
public:
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::HEdgePointer HEdgePointer;
|
||||
typedef typename MeshType::HEdgeType HEdgeType;
|
||||
typedef typename MeshType::EdgePointer EdgePointer;
|
||||
typedef typename MeshType::EdgeType EdgeType;
|
||||
typedef typename MeshType::EdgeIterator EdgeIterator;
|
||||
typedef typename MeshType::HEdgeIterator HEdgeIterator;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
|
||||
struct VertexPairEdgePtr{
|
||||
VertexPairEdgePtr(VertexPointer _v0,VertexPointer _v1,HEdgePointer _ep):v0(_v0),v1(_v1),ep(_ep){if(v0>v1) std::swap(v0,v1);}
|
||||
bool operator <(const VertexPairEdgePtr &o) const {return (v0 == o.v0)? (v1<o.v1):(v0<o.v0);}
|
||||
bool operator ==(const VertexPairEdgePtr &o) const {return (v0 == o.v0)&& (v1==o.v1);}
|
||||
|
||||
VertexPointer v0,v1;
|
||||
HEdgePointer ep;
|
||||
};
|
||||
struct FacePtrInt{
|
||||
FacePtrInt ( FaceType * _f,int _i):f(_f),i(_i){}
|
||||
FaceType * f;
|
||||
int i;
|
||||
};
|
||||
|
||||
typedef std::vector<bool> BitVector;
|
||||
|
||||
/**
|
||||
build a half-edge data structure from an indexed data structure. Note that the half-edges are allocated here for the first time.
|
||||
If you have a mesh where there are already edges, they will be removed and the data lost, so do not use this function
|
||||
to just "update" the topology of half edges.
|
||||
**/
|
||||
static void FromIndexed(MeshType & m){
|
||||
assert(HasFVAdjacency(m));
|
||||
assert(HasHOppAdjacency(m));
|
||||
assert(HasHNextAdjacency(m));
|
||||
|
||||
typename MeshType::template PerFaceAttributeHandle<BitVector> flagVisited =
|
||||
vcg::tri::Allocator<MeshType>::template AddPerFaceAttribute<BitVector>(m,"");
|
||||
std::vector<FacePtrInt > borderEdges;
|
||||
|
||||
// allocate all new half edges
|
||||
FaceIterator fi;
|
||||
unsigned int n_edges = 0;
|
||||
|
||||
// count how many half edge to allocate
|
||||
for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD())
|
||||
{n_edges+=(*fi).VN();
|
||||
for(int i = 0; i < (*fi).VN(); ++i)
|
||||
if(vcg::face::IsBorder<FaceType>((*fi),(i)))
|
||||
++n_edges;
|
||||
}
|
||||
|
||||
m.hedge.clear();
|
||||
m.hn = 0;
|
||||
|
||||
// allocate the half edges
|
||||
typename MeshType::HEdgeIterator ei = vcg::tri::Allocator<MeshType>::AddHEdges(m,n_edges);
|
||||
|
||||
|
||||
std::vector<VertexPairEdgePtr> all;
|
||||
int firstEdge = 0;
|
||||
for(fi = m.face.begin(); fi != m.face.end(); ++fi)if(!(*fi).IsD()){
|
||||
assert((*fi).VN()>2);
|
||||
if(flagVisited[*fi].empty()) {flagVisited[*fi].resize((*fi).VN());}
|
||||
|
||||
for(int i = 0; i < (*fi).VN(); ++i,++ei)
|
||||
{
|
||||
(*ei).HVp() = (*fi).V(i);
|
||||
(*ei).HNp() = &m.hedge[firstEdge + (i +1) % (*fi).VN()];
|
||||
if(MeshType::HEdgeType::HasHFAdjacency())
|
||||
(*ei).HFp() = &(*fi);
|
||||
if( MeshType::FaceType::HasFHAdjacency())
|
||||
(*fi).FHp() = &(*ei);
|
||||
if(MeshType::HEdgeType::HasHPrevAdjacency())
|
||||
(*ei).HPp() = &m.hedge[firstEdge + (i +(*fi).VN()-1) % (*fi).VN()];
|
||||
if(HasVHAdjacency(m))
|
||||
(*ei).HVp()->VHp() = &(*ei);
|
||||
all.push_back(VertexPairEdgePtr((*fi).V(i), (*fi).V((*fi).Next(i)),&(*ei)));// it will be used to link the hedges
|
||||
|
||||
if( vcg::face::IsBorder<FaceType>((*fi),(i)))
|
||||
borderEdges.push_back(FacePtrInt(&(*fi),i));
|
||||
}
|
||||
firstEdge += (*fi).VN();
|
||||
}
|
||||
|
||||
// add all the border hedges
|
||||
int borderLength;
|
||||
typename std::vector<FacePtrInt >::iterator ebi;
|
||||
for( ebi = borderEdges.begin(); ebi != borderEdges.end(); ++ebi)
|
||||
if( !flagVisited[(*ebi).f][(*ebi).i])// not already inserted
|
||||
{
|
||||
|
||||
borderLength = 0;
|
||||
vcg::face::Pos<FaceType> bp((*ebi).f,(*ebi).i);
|
||||
|
||||
//FaceType * start = (*ebi).f;
|
||||
VertexType * start = ((*ebi).f)->V((*ebi).i);
|
||||
do{
|
||||
all.push_back( VertexPairEdgePtr ( bp.f->V( bp.f->Next(bp.z) ),bp.f->V( bp.z ),&(*ei)));
|
||||
(*ei).HVp() = bp.f->V(bp.f->Next(bp.z)) ;
|
||||
flagVisited[bp.f][bp.z] = true;
|
||||
++ei;
|
||||
bp.NextB();
|
||||
++borderLength;
|
||||
}while (bp.v != start);
|
||||
//}while (bp.f != start);
|
||||
|
||||
|
||||
// run over the border edges to link the adjacencies
|
||||
for(int be = 0; be < borderLength; ++be)
|
||||
{
|
||||
if(MeshType::HEdgeType::HasHFAdjacency())
|
||||
m.hedge[firstEdge + be].HFp() = NULL;
|
||||
|
||||
if(MeshType::HEdgeType::HasHPrevAdjacency())
|
||||
m.hedge[firstEdge + be].HPp() = &m.hedge[firstEdge + (be +borderLength-1) % borderLength];
|
||||
|
||||
m.hedge[firstEdge + be].HNp() = &m.hedge[firstEdge + (be +1) % borderLength];
|
||||
}
|
||||
firstEdge+=borderLength;
|
||||
}
|
||||
|
||||
vcg::tri::Allocator<MeshType>:: template DeletePerFaceAttribute<BitVector>(m,flagVisited );
|
||||
|
||||
std::sort(all.begin(),all.end());
|
||||
assert(all.size() == n_edges);
|
||||
|
||||
for(unsigned int i = 0 ; i < all.size(); )
|
||||
if(all[i] == all[i+1])
|
||||
{
|
||||
all[i].ep->HOp() = all[i+1].ep;
|
||||
all[i+1].ep->HOp() = all[i].ep;
|
||||
i+=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
all[i].ep->HOp() = all[i].ep;
|
||||
i+=1;
|
||||
}
|
||||
|
||||
if(HasEHAdjacency(m) && HasHEAdjacency(m))
|
||||
{
|
||||
assert(m.edge.size() == 0 || m.edge.size() == n_edges/2);
|
||||
|
||||
if ( m.edge.size() == 0 )
|
||||
{
|
||||
m.en = 0;
|
||||
// allocate the edges
|
||||
typename MeshType::EdgeIterator edge_i = vcg::tri::Allocator<MeshType>::AddEdges(m,n_edges/2);
|
||||
|
||||
for(ei = m.hedge.begin(); ei != m.hedge.end(); ++ei)
|
||||
{
|
||||
if((*ei).HEp() == NULL)
|
||||
{
|
||||
(*ei).HEp() = &(*edge_i);
|
||||
(*ei).HOp()->HEp() = &(*edge_i);
|
||||
|
||||
(*edge_i).EHp() = &(*ei);
|
||||
|
||||
++edge_i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(HasEVAdjacency(m) && HasHEAdjacency(m) && HasEHAdjacency(m))
|
||||
{
|
||||
//update edge relations
|
||||
|
||||
for(typename MeshType::EdgeIterator ei1 = m.edge.begin(); ei1 != m.edge.end(); ++ei1 )
|
||||
{
|
||||
std::vector<HEdgePointer> hedges = HalfEdgeTopology<MeshType>::get_incident_hedges((*ei1).V(0));
|
||||
|
||||
for(typename std::vector<HEdgePointer>::iterator hi = hedges.begin(); hi != hedges.end(); ++hi)
|
||||
{
|
||||
if((*hi)->HOp()->HVp() == (*ei1).V(1))
|
||||
{
|
||||
|
||||
assert((*hi)->HEp() == NULL);
|
||||
assert((*hi)->HOp()->HEp() == NULL);
|
||||
|
||||
// EH
|
||||
(*ei1).EHp() = *hi;
|
||||
|
||||
// HE
|
||||
(*hi)->HEp() = &(*ei1);
|
||||
(*hi)->HOp()->HEp() = &(*ei1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Checks pointers FHEp() are valid
|
||||
**/
|
||||
static bool CheckConsistency_FHp(MeshType & m){
|
||||
assert(MeshType::FaceType::HasFHAdjacency());
|
||||
FaceIterator fi;
|
||||
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if(!(*fi).IsD()){
|
||||
if((*fi).FHp() < &(*m.hedge.begin())) return false;
|
||||
if((*fi).FHp() > &(m.hedge.back())) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Checks that half edges and face relation are consistent
|
||||
**/
|
||||
static bool CheckConsistency(MeshType & m){
|
||||
assert(MeshType::HEdgeType::HasHNextAdjacency());
|
||||
assert(MeshType::HEdgeType::HasHOppAdjacency());
|
||||
assert(MeshType::HEdgeType::HasHVAdjacency());
|
||||
assert(MeshType::FaceType::HasFHAdjacency());
|
||||
|
||||
//bool hasHEF = ( MeshType::HEdgeType::HasHFAdjacency());
|
||||
bool hasHP = ( MeshType::HEdgeType::HasHPrevAdjacency());
|
||||
|
||||
FaceIterator fi;
|
||||
HEdgePointer ep,ep1;
|
||||
int cnt = 0;
|
||||
|
||||
if( MeshType::HEdgeType::HasHFAdjacency() )
|
||||
{
|
||||
int iDb = 0;
|
||||
for(fi = m.face.begin(); fi != m.face.end(); ++fi,++iDb)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
ep = ep1 = (*fi).FHp();
|
||||
|
||||
do{
|
||||
if(ep->IsD())
|
||||
return false; // the hedge should not be connected, it has been deleted
|
||||
if( ! ep->HFp())
|
||||
return false;
|
||||
if(ep->HFp() != &(*fi))
|
||||
return false;// hedge is not pointing to the rigth face
|
||||
ep = ep->HNp();
|
||||
if(cnt++ > m.hn)
|
||||
return false; // hedges are ill connected (HENp())
|
||||
|
||||
}while(ep!=ep1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
HEdgePointer epPrev;
|
||||
HEdgeIterator hi;
|
||||
//bool extEdge ;
|
||||
for( hi = m.hedge.begin(); hi != m.hedge.end(); ++hi)
|
||||
if(!(*hi).IsD())
|
||||
{
|
||||
//cnt = 0;
|
||||
epPrev = ep = ep1 = &(*hi);
|
||||
//do{
|
||||
//extEdge = (ep->HFp()==NULL);
|
||||
if(hasHP)
|
||||
{
|
||||
if( !ep->HPp())
|
||||
return false;
|
||||
if( ep->HPp() == ep)
|
||||
return false; // the previous of an edge cannot be the edge itself
|
||||
if( ep->HNp()->HPp() != ep)
|
||||
return false; // next and prev relation are not mutual
|
||||
if( ep->HPp()->IsD())
|
||||
return false; //
|
||||
}
|
||||
|
||||
if( ! ep->HOp() )
|
||||
return false;
|
||||
|
||||
if( ep->HOp() == ep)
|
||||
return false; // opposite relation is not mutual
|
||||
|
||||
if( ep->HOp()->IsD())
|
||||
return false;
|
||||
|
||||
if( ep->HOp()->HOp() != ep)
|
||||
return false; // opposite relation is not mutual
|
||||
|
||||
if( HasHFAdjacency(m) )
|
||||
{
|
||||
if(ep->HFp())
|
||||
{
|
||||
if( ep->HFp()->IsD())
|
||||
return false; // pointed face must not be deleted
|
||||
}
|
||||
}
|
||||
|
||||
if( HasHEAdjacency(m) && (m.en!=0))
|
||||
{
|
||||
if( ! ep->HEp())
|
||||
return false; //halfedge must point to an edge
|
||||
|
||||
if( ep->HEp()->IsD())
|
||||
return false; // pointed edge must not be deleted
|
||||
|
||||
if(ep->HEp() != ep->HOp()->HEp())
|
||||
return false; // he and opposite he must point to the same edge
|
||||
|
||||
if(ep->HEp()->EHp() != ep && ep->HEp()->EHp() != ep->HOp() )
|
||||
return false; // halfedge points to an edge not pointing it or its opposite
|
||||
|
||||
}
|
||||
|
||||
|
||||
if( !ep->HNp() )
|
||||
return false;
|
||||
|
||||
if( ep->HNp() == ep )
|
||||
return false; // the next of an hedge cannot be the hedge itself
|
||||
|
||||
if( ep->HNp()->IsD())
|
||||
return false; //
|
||||
|
||||
if(hasHP)
|
||||
if( ep->HNp()->HPp() != ep)
|
||||
return false; //
|
||||
|
||||
if( HasHVAdjacency(m) )
|
||||
{
|
||||
if( ! ep->HVp() )
|
||||
return false; // halfedge must point to a vertex
|
||||
|
||||
if( ep->HVp()->IsD() )
|
||||
return false; // pointed vertex must not be deleted
|
||||
|
||||
if( HasVHAdjacency(m) )
|
||||
if( ! (ep->HVp()->VHp()) )
|
||||
return false; // halfedge points to a vertex pointing NULL
|
||||
|
||||
}
|
||||
|
||||
|
||||
ep = ep->HNp();
|
||||
if( ep->HVp() != epPrev->HOp()->HVp())
|
||||
return false;
|
||||
|
||||
epPrev = ep;
|
||||
|
||||
// if(cnt++ > m.hn)
|
||||
// return false; // edges are ill connected (HENp())
|
||||
|
||||
//}while(ep!=ep1);
|
||||
}
|
||||
|
||||
if(HasEHAdjacency(m) && HasHEAdjacency(m))
|
||||
for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
|
||||
{
|
||||
if(!(*ei).IsD())
|
||||
{
|
||||
if( !(*ei).EHp())
|
||||
return false; //edge must have a valid pointer to his halfedge
|
||||
|
||||
if( (*ei).EHp()->HEp() != &(*ei) )
|
||||
return false; // edge's halfedge must point to the edge itself
|
||||
|
||||
if( (*ei).EHp()->IsD())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(HasVHAdjacency(m))
|
||||
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
{
|
||||
if( !(*vi).IsD() )
|
||||
if( (*vi).VHp() )
|
||||
{
|
||||
if( (*vi).VHp()->HVp() != &(*vi) )
|
||||
return false;
|
||||
if( (*vi).VHp()->IsD())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Set the relations HFp(), FHp() from a loop of edges to a face
|
||||
*/
|
||||
private:
|
||||
static void SetRelationsLoopFace(HEdgeType * e0, FaceType * f){
|
||||
assert(HEdgeType::HasHNextAdjacency());
|
||||
assert(FaceType::HasFHAdjacency());
|
||||
|
||||
HEdgeType *e = e0;
|
||||
assert(e!=NULL);
|
||||
do{ e->HFp() = f; e = e->HNp(); } while(e != e0);
|
||||
f->FHp() = e0;
|
||||
}
|
||||
|
||||
/**
|
||||
Merge the two faces. This will probably become a class template or a functor
|
||||
*/
|
||||
static void MergeFaces(FaceType *, FaceType *){}
|
||||
|
||||
/**
|
||||
Find previous hedge in the loop
|
||||
*/
|
||||
static HEdgeType * PreviousEdge(HEdgeType * e0){
|
||||
HEdgeType * ep = e0;
|
||||
do{
|
||||
if(ep->HNp() == e0) return ep;
|
||||
ep = ep->HNp();
|
||||
}while(ep!=e0);
|
||||
assert(0); // degenerate loop
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
/** Adds an edge between the sources of e0 and e1 and set all the topology relations.
|
||||
If the edges store the pointers to the faces then a new face is created.
|
||||
<--- e1 ---- X <------e1_HEPp---
|
||||
^
|
||||
||
|
||||
ei0 || ei1
|
||||
||
|
||||
v
|
||||
----e0_HEPp-> X ----- e0 ------>
|
||||
*/
|
||||
static void AddHEdge(MeshType &m, HEdgeType * e0, HEdgeType * e1){
|
||||
assert(e1!=e0->HNp());
|
||||
assert(e0!=e1->HNp());
|
||||
bool hasP = MeshType::HEdgeType::HasHPrevAdjacency();
|
||||
assert(e0->HOp() != e1); // the hedge already exists
|
||||
assert(e0!=e1->HNp());
|
||||
|
||||
std::vector<typename MeshType::HEdgePointer* > toUpdate;
|
||||
toUpdate.push_back(&e0);
|
||||
toUpdate.push_back(&e1);
|
||||
HEdgeIterator ei0 = vcg::tri::Allocator<MeshType>::AddHEdges(m,2,toUpdate);
|
||||
|
||||
HEdgeIterator ei1 = ei0; ++ei1;
|
||||
(*ei0).HNp() = e1;(*ei0).HVp() = e0->HVp();
|
||||
(*ei1).HNp() = e0;(*ei1).HVp() = e1->HVp();
|
||||
|
||||
HEdgePointer e0_HEPp = 0,e1_HEPp = 0,ep =0;
|
||||
if(hasP){
|
||||
e0_HEPp = e0->HPp();
|
||||
e1_HEPp = e1->HPp();
|
||||
}else{// does not have pointer to previous, it must be computed
|
||||
ep = e0;
|
||||
do{
|
||||
if(ep->HNp() == e0) e0_HEPp = ep;
|
||||
if(ep->HNp() == e1) e1_HEPp = ep;
|
||||
ep = ep->HNp();
|
||||
}while(ep!=e0);
|
||||
}
|
||||
if(hasP){
|
||||
(*ei0).HPp() = e0->HPp();
|
||||
(*ei1).HPp() = e1->HPp();
|
||||
e0->HPp() = &(*ei1);
|
||||
e1->HPp() = &(*ei0);
|
||||
}
|
||||
e0_HEPp -> HNp() = &(*ei0);
|
||||
e1_HEPp -> HNp() = &(*ei1);
|
||||
|
||||
(*ei0).HOp() = &(*ei1);
|
||||
(*ei1).HOp() = &(*ei0);
|
||||
|
||||
|
||||
if( HEdgeType::HasHFAdjacency() && FaceType::HasFHAdjacency()){
|
||||
vcg::tri::Allocator<MeshType>::AddFaces(m,1);
|
||||
m.face.back().ImportData(*e0->HFp());
|
||||
SetRelationsLoopFace(&(*ei0),e1->HFp()); // one loop to the old face
|
||||
SetRelationsLoopFace(&(*ei1),&m.face.back()); // the other to the new face
|
||||
}
|
||||
}
|
||||
|
||||
/** Detach the topology relations of a given edge
|
||||
<--- e->HENPp -X --- <---------eO_HEPp---
|
||||
^
|
||||
||
|
||||
e || e->HEOp()
|
||||
||
|
||||
v
|
||||
----e_HEPp--> X ----- e->HEOp->HENPp() ------>
|
||||
|
||||
*/
|
||||
static void RemoveHEdge(MeshType &m, HEdgeType * e){
|
||||
assert(MeshType::HEdgeType::HasHNextAdjacency());
|
||||
assert(MeshType::HEdgeType::HasHOppAdjacency());
|
||||
assert(MeshType::FaceType::HasFHAdjacency());
|
||||
|
||||
bool hasP = MeshType::HEdgeType::HasHPrevAdjacency();
|
||||
HEdgePointer e_HEPp,eO_HEPp;
|
||||
|
||||
if(hasP){
|
||||
e_HEPp = e->HPp();
|
||||
eO_HEPp = e->HOp()->HPp();
|
||||
}else{
|
||||
e_HEPp = PreviousEdge(e);
|
||||
eO_HEPp = PreviousEdge(e->HOp());
|
||||
}
|
||||
|
||||
assert(e_HEPp->HNp() == e);
|
||||
assert(eO_HEPp->HNp() == e->HOp());
|
||||
e_HEPp->HNp() = e->HOp()->HNp();
|
||||
eO_HEPp->HNp() = e-> HNp();
|
||||
|
||||
if(hasP) {
|
||||
e->HOp()->HNp()->HPp() = e_HEPp;
|
||||
e->HNp()->HPp() = eO_HEPp;
|
||||
|
||||
e->HPp() = NULL;
|
||||
e-> HOp()->HPp() = NULL;
|
||||
}
|
||||
|
||||
|
||||
// take care of the faces
|
||||
if(MeshType::HEdgeType::HasHFAdjacency()){
|
||||
MergeFaces(e_HEPp->HFp(),eO_HEPp->HFp());
|
||||
vcg::tri::Allocator<MeshType>::DeleteFace(m,*eO_HEPp->HFp());
|
||||
SetRelationsLoopFace(e_HEPp,e_HEPp->HFp());
|
||||
|
||||
}
|
||||
vcg::tri::Allocator<MeshType>::DeleteHEdge(m,*e->HOp());
|
||||
vcg::tri::Allocator<MeshType>::DeleteHEdge(m,*e);
|
||||
|
||||
}
|
||||
|
||||
};// end class
|
||||
template <class MeshType >
|
||||
struct UpdateIndexed{
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::HEdgePointer HEdgePointer;
|
||||
typedef typename MeshType::HEdgeType HEdgeType;
|
||||
typedef typename MeshType::HEdgeIterator HEdgeIterator;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
|
||||
struct VertexPairEdgePtr{
|
||||
VertexPairEdgePtr(VertexPointer _v0,VertexPointer _v1,HEdgePointer _ep):v0(_v0),v1(_v1),ep(_ep){if(v0>v1) std::swap(v0,v1);}
|
||||
bool operator <(const VertexPairEdgePtr &o) const {return (v0 == o.v0)? (v1<o.v1):(v0<o.v0);}
|
||||
bool operator ==(const VertexPairEdgePtr &o) const {return (v0 == o.v0)&& (v1==o.v1);}
|
||||
|
||||
VertexPointer v0,v1;
|
||||
HEdgePointer ep;
|
||||
};
|
||||
|
||||
/**
|
||||
builds an indexed data structure from a half-edge data structure.
|
||||
Note: if the half edge have the pointer to face
|
||||
their relation FV (face-vertex) will be computed and the data possibly stored in the
|
||||
face will be preserved.
|
||||
**/
|
||||
static void FromHalfEdges( MeshType & m ){
|
||||
assert(HasFVAdjacency(m));
|
||||
assert(MeshType::HEdgeType::HasHNextAdjacency());
|
||||
assert(MeshType::HEdgeType::HasHVAdjacency());
|
||||
assert(MeshType::HEdgeType::HasHOppAdjacency());
|
||||
assert(MeshType::FaceType::HasFHAdjacency());
|
||||
bool hasHEF;
|
||||
//bool createFace,hasHEF,hasFHE;
|
||||
|
||||
// typename MeshType::template PerHEdgeAttributeHandle<bool> hV = Allocator<MeshType>::template AddPerHEdgeAttribute<bool>(m,"");
|
||||
|
||||
|
||||
typename MeshType::HEdgeIterator ei;
|
||||
typename MeshType::FacePointer fp;
|
||||
typename MeshType::FaceIterator fi;
|
||||
typename MeshType::HEdgePointer ep,epF;
|
||||
//int vi = 0;
|
||||
vcg::SimpleTempData<typename MeshType::HEdgeContainer,bool> hV(m.hedge);
|
||||
|
||||
hasHEF = (MeshType::HEdgeType::HasHFAdjacency());
|
||||
assert( !hasHEF || (hasHEF && m.fn>0));
|
||||
|
||||
// if the edgetype has the pointer to face
|
||||
// it is assumed the the edget2face pointer (HEFp) are correct
|
||||
// and the faces are allocated
|
||||
for ( ei = m.hedge.begin(); ei != m.hedge.end(); ++ei)
|
||||
if(!(*ei).IsD()) // it has not been deleted
|
||||
if(!hasHEF || ( hasHEF && (*ei).HFp()!=NULL)) // if it has a pointer to the face it is
|
||||
// not null (i.e. it is not a border edge)
|
||||
if(!hV[(*ei)] ) // it has not be visited yet
|
||||
{
|
||||
if(!hasHEF)// if it has
|
||||
fp = &(* Allocator<MeshType>::AddFaces(m,1));
|
||||
else
|
||||
fp = (*ei).HFp();
|
||||
|
||||
ep = epF = &(*ei);
|
||||
std::vector<VertexPointer> vpts;
|
||||
do{vpts.push_back((*ep).HVp()); ep=ep->HNp();}while(ep!=epF);
|
||||
//int idbg =fp->VN();
|
||||
if(size_t(fp->VN()) != vpts.size()){
|
||||
fp->Dealloc();
|
||||
fp ->Alloc(vpts.size());
|
||||
}
|
||||
//int idbg1 =fp->VN();
|
||||
for(size_t i = 0; i < vpts.size();++i) fp ->V(i) = vpts[i];// set the pointer from face to vertex
|
||||
|
||||
hV[(*ei)] = true;
|
||||
}
|
||||
//Allocator<MeshType>::DeletePerHEdgeAttribute(m,hV);
|
||||
}
|
||||
|
||||
};
|
||||
} // end namespace vcg
|
||||
}
|
||||
#endif // __VCGLIB_EDGE_SUPPORT
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,464 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_NORMALS
|
||||
#define __VCG_TRI_UPDATE_NORMALS
|
||||
|
||||
#include <vcg/space/triangle3.h>
|
||||
#include <vcg/complex/base.h>
|
||||
|
||||
#include <vcg/complex/algorithms/polygon_support.h>
|
||||
|
||||
#include "flag.h"
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile normal.h vcg/complex/algorithms/update/normal.h
|
||||
|
||||
/// \brief Management, updating and computation of per-vertex, per-face, and per-wedge normals.
|
||||
/**
|
||||
This class is used to compute or to update the normals that can be stored in the various component of a mesh.
|
||||
A number of different algorithms for computing per vertex normals are present.
|
||||
|
||||
It must be included \b after complex.h
|
||||
*/
|
||||
|
||||
template <class ComputeMeshType>
|
||||
class UpdateNormal
|
||||
{
|
||||
public:
|
||||
typedef ComputeMeshType MeshType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::CoordType CoordType;
|
||||
typedef typename VertexType::NormalType NormalType;
|
||||
typedef typename VertexType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
|
||||
/// \brief Set to zero all the PerVertex normals
|
||||
/**
|
||||
Set to zero all the PerVertex normals. Used by all the face averaging algorithms.
|
||||
by default it does not clear the normals of unreferenced vertices because they could be still useful
|
||||
*/
|
||||
static void PerVertexClear(ComputeMeshType &m, bool ClearAllVertNormal=false)
|
||||
{
|
||||
RequirePerVertexNormal(m);
|
||||
if(ClearAllVertNormal)
|
||||
UpdateFlags<ComputeMeshType>::VertexClearV(m);
|
||||
else
|
||||
{
|
||||
UpdateFlags<ComputeMeshType>::VertexSetV(m);
|
||||
for(FaceIterator f=m.face.begin();f!=m.face.end();++f)
|
||||
if( !(*f).IsD() )
|
||||
for(int i=0;i<3;++i) (*f).V(i)->ClearV();
|
||||
}
|
||||
VertexIterator vi;
|
||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if( !(*vi).IsD() && (*vi).IsRW() && (!(*vi).IsV()) )
|
||||
(*vi).N() = NormalType((ScalarType)0,(ScalarType)0,(ScalarType)0);
|
||||
}
|
||||
|
||||
/// \brief Calculates the vertex normal as the classic area weighted average. It does not need or exploit current face normals.
|
||||
/**
|
||||
The normal of a vertex v is the classical area-weigthed average of the normals of the faces incident on v.
|
||||
*/
|
||||
static void PerVertex(ComputeMeshType &m)
|
||||
{
|
||||
PerVertexClear(m);
|
||||
for(FaceIterator f=m.face.begin();f!=m.face.end();++f)
|
||||
if( !(*f).IsD() && (*f).IsR() )
|
||||
{
|
||||
typename VertexType::NormalType t = vcg::TriangleNormal(*f);
|
||||
|
||||
for(int j=0; j<(*f).VN(); ++j)
|
||||
if( !(*f).V(j)->IsD() && (*f).V(j)->IsRW() )
|
||||
(*f).V(j)->N() += t;
|
||||
}
|
||||
}
|
||||
|
||||
static void PerFacePolygonal(ComputeMeshType &m)
|
||||
{
|
||||
RequirePerFaceNormal(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
{
|
||||
if( !(*fi).IsD() )
|
||||
fi->N() = PolygonNormal(*fi).Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Calculates the vertex normal as an angle weighted average. It does not need or exploit current face normals.
|
||||
/**
|
||||
The normal of a vertex v computed as a weighted sum f the incident face normals.
|
||||
The weight is simlply the angle of the involved wedge. Described in:
|
||||
|
||||
G. Thurmer, C. A. Wuthrich
|
||||
"Computing vertex normals from polygonal facets"
|
||||
Journal of Graphics Tools, 1998
|
||||
*/
|
||||
static void PerVertexAngleWeighted(ComputeMeshType &m)
|
||||
{
|
||||
PerVertexClear(m);
|
||||
FaceIterator f;
|
||||
for(f=m.face.begin();f!=m.face.end();++f)
|
||||
if( !(*f).IsD() && (*f).IsR() )
|
||||
{
|
||||
NormalType t = TriangleNormal(*f).Normalize();
|
||||
NormalType e0 = ((*f).V1(0)->cP()-(*f).V0(0)->cP()).Normalize();
|
||||
NormalType e1 = ((*f).V1(1)->cP()-(*f).V0(1)->cP()).Normalize();
|
||||
NormalType e2 = ((*f).V1(2)->cP()-(*f).V0(2)->cP()).Normalize();
|
||||
|
||||
(*f).V(0)->N() += t*AngleN(e0,-e2);
|
||||
(*f).V(1)->N() += t*AngleN(-e0,e1);
|
||||
(*f).V(2)->N() += t*AngleN(-e1,e2);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Calculates the vertex normal using the Max et al. weighting scheme. It does not need or exploit current face normals.
|
||||
/**
|
||||
The normal of a vertex v is computed according to the formula described by Nelson Max in
|
||||
Max, N., "Weights for Computing Vertex Normals from Facet Normals", Journal of Graphics Tools, 4(2) (1999)
|
||||
|
||||
The weight for each wedge is the cross product of the two edge over the product of the square of the two edge lengths.
|
||||
According to the original paper it is perfect only for spherical surface, but it should perform well...
|
||||
*/
|
||||
static void PerVertexNelsonMaxWeighted(ComputeMeshType &m)
|
||||
{
|
||||
PerVertexClear(m);
|
||||
FaceIterator f;
|
||||
for(f=m.face.begin();f!=m.face.end();++f)
|
||||
if( !(*f).IsD() && (*f).IsR() )
|
||||
{
|
||||
typename FaceType::NormalType t = TriangleNormal(*f);
|
||||
ScalarType e0 = SquaredDistance((*f).V0(0)->cP(),(*f).V1(0)->cP());
|
||||
ScalarType e1 = SquaredDistance((*f).V0(1)->cP(),(*f).V1(1)->cP());
|
||||
ScalarType e2 = SquaredDistance((*f).V0(2)->cP(),(*f).V1(2)->cP());
|
||||
|
||||
(*f).V(0)->N() += t/(e0*e2);
|
||||
(*f).V(1)->N() += t/(e0*e1);
|
||||
(*f).V(2)->N() += t/(e1*e2);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Calculates the face normal
|
||||
///
|
||||
/// Not normalized. Use PerFaceNormalized() or call NormalizePerVertex() if you need unit length per face normals.
|
||||
static void PerFace(ComputeMeshType &m)
|
||||
{
|
||||
RequirePerFaceNormal(m);
|
||||
for(FaceIterator f=m.face.begin();f!=m.face.end();++f)
|
||||
if( !(*f).IsD() )
|
||||
f->N() = TriangleNormal(*f);
|
||||
}
|
||||
|
||||
|
||||
/// \brief computePerPolygonalFace computes the normal of each polygonal face.
|
||||
///
|
||||
/// Not normalized. Use PerPolygonalFaceNormalized() or call NormalizePerFace() if you need unit length per face normals.
|
||||
static void PerPolygonalFace(ComputeMeshType &m) {
|
||||
tri::RequirePerFaceNormal(m);
|
||||
tri::RequirePolygonalMesh(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); fi++)
|
||||
if (!fi->IsD()) {
|
||||
fi->N().SetZero();
|
||||
for (int i = 0; i < fi->VN(); i++)
|
||||
fi->N() += fi->V0(i)->P() ^ fi->V1(i)->P();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \brief Calculates the vertex normal by averaging the current per-face normals.
|
||||
/**
|
||||
The normal of a vertex v is the average of the un-normalized normals of the faces incident on v.
|
||||
*/
|
||||
static void PerVertexFromCurrentFaceNormal(ComputeMeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexNormal(m);
|
||||
|
||||
VertexIterator vi;
|
||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if( !(*vi).IsD() && (*vi).IsRW() )
|
||||
(*vi).N()=CoordType(0,0,0);
|
||||
|
||||
FaceIterator fi;
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if( !(*fi).IsD())
|
||||
{
|
||||
for(int j=0; j<(*fi).VN(); ++j)
|
||||
if( !(*fi).V(j)->IsD())
|
||||
(*fi).V(j)->N() += (*fi).cN();
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Calculates the face normal by averaging the current per-vertex normals.
|
||||
/**
|
||||
The normal of a face f is the average of the normals of the vertices of f.
|
||||
*/
|
||||
static void PerFaceFromCurrentVertexNormal(ComputeMeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexNormal(m);
|
||||
tri::RequirePerFaceNormal(m);
|
||||
for (FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi)
|
||||
if( !(*fi).IsD())
|
||||
{
|
||||
NormalType n;
|
||||
n.SetZero();
|
||||
for(int j=0; j<3; ++j)
|
||||
n += fi->V(j)->cN();
|
||||
n.Normalize();
|
||||
fi->N() = n;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Normalize the length of the vertex normals.
|
||||
static void NormalizePerVertex(ComputeMeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexNormal(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if( !(*vi).IsD() && (*vi).IsRW() )
|
||||
(*vi).N().Normalize();
|
||||
}
|
||||
|
||||
/// \brief Normalize the length of the face normals.
|
||||
static void NormalizePerFace(ComputeMeshType &m)
|
||||
{
|
||||
tri::RequirePerFaceNormal(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if( !(*fi).IsD() ) (*fi).N().Normalize();
|
||||
}
|
||||
|
||||
/// \brief Set the length of the face normals to their area (without recomputing their directions).
|
||||
static void NormalizePerFaceByArea(ComputeMeshType &m)
|
||||
{
|
||||
tri::RequirePerFaceNormal(m);
|
||||
FaceIterator fi;
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if( !(*fi).IsD() )
|
||||
{
|
||||
(*fi).N().Normalize();
|
||||
(*fi).N() = (*fi).N() * DoubleArea(*fi);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Equivalent to PerVertex() and NormalizePerVertex()
|
||||
static void PerVertexNormalized(ComputeMeshType &m)
|
||||
{
|
||||
PerVertex(m);
|
||||
NormalizePerVertex(m);
|
||||
}
|
||||
|
||||
/// \brief Equivalent to PerFace() and NormalizePerFace()
|
||||
static void PerFaceNormalized(ComputeMeshType &m)
|
||||
{
|
||||
PerFace(m);
|
||||
NormalizePerFace(m);
|
||||
}
|
||||
|
||||
/// \brief Equivalent to PerPolygonalFace() and NormalizePerFace()
|
||||
static void PerPolygonalFaceNormalized(ComputeMeshType &m) {
|
||||
PerPolygonalFace(m);
|
||||
NormalizePerFace(m);
|
||||
}
|
||||
|
||||
/// \brief Equivalent to PerVertex() and PerFace().
|
||||
static void PerVertexPerFace(ComputeMeshType &m)
|
||||
{
|
||||
PerFace(m);
|
||||
PerVertex(m);
|
||||
}
|
||||
|
||||
/// \brief Equivalent to PerVertexNormalized() and PerFace().
|
||||
static void PerVertexNormalizedPerFace(ComputeMeshType &m)
|
||||
{
|
||||
PerVertexPerFace(m);
|
||||
NormalizePerVertex(m);
|
||||
}
|
||||
|
||||
/// \brief Equivalent to PerVertexNormalizedPerFace() and NormalizePerFace().
|
||||
static void PerVertexNormalizedPerFaceNormalized(ComputeMeshType &m)
|
||||
{
|
||||
PerVertexNormalizedPerFace(m);
|
||||
NormalizePerFace(m);
|
||||
}
|
||||
|
||||
/// \brief Exploit bitquads to compute a per-polygon face normal
|
||||
static void PerBitQuadFaceNormalized(ComputeMeshType &m)
|
||||
{
|
||||
PerFace(m);
|
||||
for(FaceIterator f=m.face.begin();f!=m.face.end();++f) {
|
||||
if( !(*f).IsD() ) {
|
||||
for (int k=0; k<3; k++) if (f->IsF(k))
|
||||
if (&*f < f->FFp(k)) {
|
||||
f->N() = f->FFp(k)->N() = (f->FFp(k)->N() + f->N()).Normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \brief Exploit bitquads to compute a per-polygon face normal
|
||||
static void PerBitPolygonFaceNormalized(ComputeMeshType &m)
|
||||
{
|
||||
PerFace(m);
|
||||
tri::RequireCompactness(m);
|
||||
tri::RequireTriangularMesh(m);
|
||||
tri::UpdateFlags<ComputeMeshType>::FaceClearV(m);
|
||||
std::vector<VertexPointer> vertVec;
|
||||
std::vector<FacePointer> faceVec;
|
||||
for(size_t i=0;i<m.face.size();++i)
|
||||
if(!m.face[i].IsV())
|
||||
{
|
||||
tri::PolygonSupport<MeshType,MeshType>::ExtractPolygon(&(m.face[i]),vertVec,faceVec);
|
||||
CoordType nf(0,0,0);
|
||||
for(size_t j=0;j<faceVec.size();++j)
|
||||
nf+=faceVec[j]->N().Normalize() * DoubleArea(*faceVec[j]);
|
||||
|
||||
nf.Normalize();
|
||||
|
||||
for(size_t j=0;j<faceVec.size();++j)
|
||||
faceVec[j]->N()=nf;
|
||||
}
|
||||
}
|
||||
/// \brief Multiply the vertex normals by the matrix passed. By default, the scale component is removed.
|
||||
static void PerVertexMatrix(ComputeMeshType &m, const Matrix44<ScalarType> &mat, bool remove_scaling= true)
|
||||
{
|
||||
tri::RequirePerVertexNormal(m);
|
||||
ScalarType scale;
|
||||
|
||||
Matrix33<ScalarType> mat33(mat,3);
|
||||
|
||||
|
||||
if(remove_scaling){
|
||||
scale = pow(mat33.Determinant(),(ScalarType)(1.0/3.0));
|
||||
Point3<ScalarType> scaleV(scale,scale,scale);
|
||||
Matrix33<ScalarType> S;
|
||||
S.SetDiagonal(scaleV.V());
|
||||
mat33*=S;
|
||||
}
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if( !(*vi).IsD() && (*vi).IsRW() )
|
||||
(*vi).N() = mat33*(*vi).N();
|
||||
}
|
||||
|
||||
/// \brief Multiply the face normals by the matrix passed. By default, the scale component is removed.
|
||||
static void PerFaceMatrix(ComputeMeshType &m, const Matrix44<ScalarType> &mat, bool remove_scaling= true)
|
||||
{
|
||||
tri::RequirePerFaceNormal(m);
|
||||
ScalarType scale;
|
||||
|
||||
Matrix33<ScalarType> mat33(mat,3);
|
||||
|
||||
if( !HasPerFaceNormal(m)) return;
|
||||
|
||||
if(remove_scaling){
|
||||
scale = pow(mat33.Determinant(),ScalarType(1.0/3.0));
|
||||
mat33[0][0]/=scale;
|
||||
mat33[1][1]/=scale;
|
||||
mat33[2][2]/=scale;
|
||||
}
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if( !(*fi).IsD() && (*fi).IsRW() )
|
||||
(*fi).N() = mat33* (*fi).N();
|
||||
}
|
||||
|
||||
/// \brief Compute per wedge normals taking into account the angle between adjacent faces.
|
||||
///
|
||||
/// The PerWedge normals are averaged on common vertexes only if the angle between two faces is \b larger than \p angleRad.
|
||||
/// It requires FFAdjacency.
|
||||
static void PerWedgeCrease(ComputeMeshType &m, ScalarType angleRad)
|
||||
{
|
||||
tri::RequirePerFaceWedgeNormal(m);
|
||||
tri::RequireFFAdjacency(m);
|
||||
|
||||
ScalarType cosangle=math::Cos(angleRad);
|
||||
|
||||
// Clear the per wedge normals
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
{
|
||||
(*fi).WN(0)=NormalType(0,0,0);
|
||||
(*fi).WN(1)=NormalType(0,0,0);
|
||||
(*fi).WN(2)=NormalType(0,0,0);
|
||||
}
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
{
|
||||
NormalType nn= TriangleNormal(*fi);
|
||||
for(int i=0;i<3;++i)
|
||||
{
|
||||
const NormalType &na=TriangleNormal(*(*fi).FFp(i));
|
||||
if(nn*na > cosangle )
|
||||
{
|
||||
fi->WN((i+0)%3) +=na;
|
||||
fi->WN((i+1)%3) +=na;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void PerFaceRW(ComputeMeshType &m, bool normalize=false)
|
||||
{
|
||||
tri::RequirePerFaceNormal(m);
|
||||
FaceIterator f;
|
||||
bool cn = true;
|
||||
|
||||
if(normalize)
|
||||
{
|
||||
for(f=m.m.face.begin();f!=m.m.face.end();++f)
|
||||
if( !(*f).IsD() && (*f).IsRW() )
|
||||
{
|
||||
for(int j=0; j<3; ++j)
|
||||
if( !(*f).V(j)->IsR()) cn = false;
|
||||
if( cn ) f->N() = TriangleNormal(*f).Normalize();
|
||||
cn = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(f=m.m.face.begin();f!=m.m.face.end();++f)
|
||||
if( !(*f).IsD() && (*f).IsRW() )
|
||||
{
|
||||
for(int j=0; j<3; ++j)
|
||||
if( !(*f).V(j)->IsR()) cn = false;
|
||||
|
||||
if( cn )
|
||||
f->N() = TriangleNormal(*f).Normalize();
|
||||
cn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
/****************************************************************************
|
||||
* 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. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
/****************************************************************************
|
||||
History
|
||||
|
||||
$Log: not supported by cvs2svn $
|
||||
Revision 1.1 2005/07/06 08:02:27 cignoni
|
||||
Initial commit
|
||||
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef __VCG_TRI_UPDATE_POSITION
|
||||
#define __VCG_TRI_UPDATE_POSITION
|
||||
|
||||
#include "normal.h"
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile position.h vcg/complex/algorithms/update/position.h
|
||||
|
||||
/// \brief This class is used to update vertex position according to a transformation matrix.
|
||||
template <class ComputeMeshType>
|
||||
class UpdatePosition
|
||||
{
|
||||
|
||||
public:
|
||||
typedef ComputeMeshType MeshType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
|
||||
/// \brief Multiply
|
||||
static void Matrix(ComputeMeshType &m, const Matrix44<ScalarType> &M, bool update_also_normals = true)
|
||||
{
|
||||
VertexIterator vi;
|
||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD()) (*vi).P()=M*(*vi).cP();
|
||||
|
||||
if(update_also_normals){
|
||||
if(HasPerVertexNormal(m)){
|
||||
UpdateNormal<ComputeMeshType>::PerVertexMatrix(m,M);
|
||||
}
|
||||
if(HasPerFaceNormal(m)){
|
||||
UpdateNormal<ComputeMeshType>::PerFaceMatrix(m,M);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Translate(ComputeMeshType &m, const Point3<ScalarType> &t)
|
||||
{
|
||||
VertexIterator vi;
|
||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD()) (*vi).P()+=t;
|
||||
}
|
||||
|
||||
static void Scale(ComputeMeshType &m, const ScalarType s)
|
||||
{
|
||||
Scale(m,Point3<ScalarType>(s,s,s));
|
||||
}
|
||||
|
||||
static void Scale(ComputeMeshType &m, const Point3<ScalarType> &s)
|
||||
{
|
||||
VertexIterator vi;
|
||||
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD()) {
|
||||
(*vi).P()[0]*=s[0];
|
||||
(*vi).P()[1]*=s[1];
|
||||
(*vi).P()[2]*=s[2];
|
||||
}
|
||||
}
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,542 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_QUALITY
|
||||
#define __VCG_TRI_UPDATE_QUALITY
|
||||
#include <vcg/complex/algorithms/stat.h>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile quality.h vcg/complex/algorithms/update/quality.h
|
||||
|
||||
/// \brief Generation of per-vertex and per-face qualities.
|
||||
/**
|
||||
It works according to various strategy, like geodesic distance from the border (UpdateQuality::VertexGeodesicFromBorder) or curvature ecc.
|
||||
This class is templated over the mesh and (like all other Update* classes) has only static members; Typical usage:
|
||||
\code
|
||||
MyMeshType m;
|
||||
UpdateQuality<MyMeshType>::VertexGeodesicFromBorder(m);
|
||||
\endcode
|
||||
*/
|
||||
|
||||
template <class UpdateMeshType>
|
||||
class UpdateQuality
|
||||
{
|
||||
public:
|
||||
typedef UpdateMeshType MeshType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::CoordType CoordType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::VertexType::QualityType VertexQualityType;
|
||||
typedef typename MeshType::FaceType::QualityType FaceQualityType;
|
||||
typedef typename MeshType::TetraType TetraType;
|
||||
typedef typename MeshType::TetraPointer TetraPointer;
|
||||
typedef typename MeshType::TetraIterator TetraIterator;
|
||||
typedef typename MeshType::TetraType::QualityType TetraQualityType;
|
||||
|
||||
|
||||
|
||||
/** Assign to each vertex of the mesh a constant quality value. Useful for initialization.
|
||||
*/
|
||||
static void VertexConstant(MeshType &m, VertexQualityType q)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q()=q;
|
||||
}
|
||||
|
||||
/** Assign to each vertex of the mesh the valence of faces.
|
||||
*/
|
||||
static void VertexValence(UpdateMeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
VertexConstant(m,0);
|
||||
for (size_t i=0;i<m.face.size();i++)
|
||||
{
|
||||
if (m.face[i].IsD())continue;
|
||||
|
||||
for (int j=0;j<m.face[i].VN();j++)
|
||||
{
|
||||
VertexType *v=m.face[i].V(j);
|
||||
v->Q()+=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Clamp each vertex of the mesh with a range of values.
|
||||
*/
|
||||
static void VertexClamp(MeshType &m,
|
||||
VertexQualityType qmin,
|
||||
VertexQualityType qmax)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q()=std::min(qmax, std::max(qmin,(*vi).Q()));
|
||||
}
|
||||
|
||||
/** Normalize the vertex quality so that it fits in the specified range.
|
||||
*/
|
||||
static void VertexNormalize(MeshType &m, VertexQualityType qmin=0.0, VertexQualityType qmax=1.0)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
ScalarType deltaRange = qmax-qmin;
|
||||
std::pair<ScalarType,ScalarType> minmax = tri::Stat<MeshType>::ComputePerVertexQualityMinMax(m);
|
||||
VertexIterator vi;
|
||||
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
(*vi).Q() = qmin+deltaRange*((*vi).Q() - minmax.first)/(minmax.second - minmax.first);
|
||||
}
|
||||
|
||||
/** Normalize the face quality so that it fits in the specified range.
|
||||
*/
|
||||
static void FaceNormalize(MeshType &m, FaceQualityType qmin=0.0, FaceQualityType qmax=1.0)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
FaceQualityType deltaRange = qmax-qmin;
|
||||
std::pair<FaceQualityType,FaceQualityType> minmax = tri::Stat<MeshType>::ComputePerFaceQualityMinMax(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
(*fi).Q() = qmin+deltaRange*((*fi).Q() - minmax.first)/(minmax.second - minmax.first);
|
||||
}
|
||||
|
||||
/** Assign to each face of the mesh a constant quality value. Useful for initialization.
|
||||
*/
|
||||
static void FaceConstant(MeshType &m, FaceQualityType q)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
(*fi).Q()=q;
|
||||
}
|
||||
|
||||
/** Assign to each face of the mesh its area.
|
||||
*/
|
||||
static void FaceArea(MeshType &m)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
(*fi).Q()=FaceQualityType(vcg::DoubleArea(*fi)/ScalarType(2.0));
|
||||
}
|
||||
|
||||
static void TetraConstant(MeshType & m, const TetraQualityType q)
|
||||
{
|
||||
tri::RequirePerTetraQuality(m);
|
||||
ForEachTetra(m, [&q] (TetraType & t) {
|
||||
t.Q() = q;
|
||||
});
|
||||
}
|
||||
static void TetraFromVolume(MeshType & m)
|
||||
{
|
||||
tri::RequirePerTetraQuality(m);
|
||||
ForEachTetra(m, [] (TetraType & t) {
|
||||
t.Q() = TetraQualityType(vcg::Tetra::ComputeVolume(t));
|
||||
});
|
||||
}
|
||||
|
||||
static void TetraFromAspectRatio(MeshType & m)
|
||||
{
|
||||
tri::RequirePerTetraQuality(m);
|
||||
ForEachTetra(m, [] (TetraType & t) {
|
||||
t.Q() = TetraQualityType(vcg::Tetra::AspectRatio(t));
|
||||
});
|
||||
}
|
||||
|
||||
static void VertexFromFace( MeshType &m, bool areaWeighted=true)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
tri::RequirePerVertexQuality(m);
|
||||
SimpleTempData<typename MeshType::VertContainer, ScalarType> TQ(m.vert,0);
|
||||
SimpleTempData<typename MeshType::VertContainer, ScalarType> TCnt(m.vert,0);
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
VertexQualityType weight=1.0;
|
||||
if(areaWeighted) weight = vcg::DoubleArea(*fi);
|
||||
for(int j=0;j<3;++j)
|
||||
{
|
||||
TQ[(*fi).V(j)]+=(*fi).Q()*weight;
|
||||
TCnt[(*fi).V(j)]+=weight;
|
||||
}
|
||||
}
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD() && TCnt[*vi]>0 )
|
||||
{
|
||||
(*vi).Q() = TQ[*vi] / TCnt[*vi];
|
||||
}
|
||||
}
|
||||
|
||||
static void VertexFromTetra(MeshType & m, bool volumeWeighted = true)
|
||||
{
|
||||
tri::RequirePerTetraQuality(m);
|
||||
tri::RequirePerVertexQuality(m);
|
||||
|
||||
SimpleTempData<typename MeshType::VertContainer, ScalarType> TQ(m.vert, 0);
|
||||
SimpleTempData<typename MeshType::VertContainer, ScalarType> TCnt(m.vert, 0);
|
||||
|
||||
ForEachTetra(m, [&] (TetraType & t) {
|
||||
TetraQualityType w = 1.;
|
||||
if (volumeWeighted)
|
||||
w = vcg::Tetra::ComputeVolume(t);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
TQ[t.V(i)] += t.Q() * w;
|
||||
TCnt[t.V(i)] += w;
|
||||
}
|
||||
});
|
||||
|
||||
ForEachVertex(m, [&] (VertexType & v) {
|
||||
v.Q() = TQ[v] / TCnt[v];
|
||||
});
|
||||
}
|
||||
|
||||
template <class HandleScalar>
|
||||
static void VertexFromAttributeHandle(MeshType &m, typename MeshType::template PerVertexAttributeHandle<HandleScalar> &h)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD())
|
||||
(*vi).Q()=VertexQualityType(h[vi]);
|
||||
}
|
||||
|
||||
static void VertexFromAttributeName(MeshType &m, const std::string &AttrName)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
auto KH = tri::Allocator<MeshType>:: template FindPerVertexAttribute<ScalarType> (m, AttrName);
|
||||
if(!tri::Allocator<MeshType>::template IsValidHandle<ScalarType>(m, KH)) throw vcg::MissingPreconditionException("Required Attribute is non existent");
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() = KH[vi];
|
||||
}
|
||||
|
||||
template <class HandleScalar>
|
||||
static void FaceFromAttributeHandle(MeshType &m, typename MeshType::template PerFaceAttributeHandle<HandleScalar> &h)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
(*fi).Q() =FaceQualityType(h[fi]);
|
||||
}
|
||||
|
||||
static void FaceFromAttributeName(MeshType &m, const std::string &AttrName)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
auto KH = tri::Allocator<MeshType>:: template FindPerFaceAttribute<ScalarType> (m, AttrName);
|
||||
if(!tri::Allocator<MeshType>::template IsValidHandle<ScalarType>(m, KH)) throw vcg::MissingPreconditionException("Required Attribute is non existent");
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
(*fi).Q() =FaceQualityType(KH[fi]);
|
||||
}
|
||||
|
||||
static void FaceFromVertex( MeshType &m)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
tri::RequirePerVertexQuality(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
{
|
||||
(*fi).Q() =0;
|
||||
for (int i=0;i<(*fi).VN();i++)
|
||||
(*fi).Q() += (*fi).V(i)->Q();
|
||||
(*fi).Q()/=(FaceQualityType)(*fi).VN();
|
||||
}
|
||||
}
|
||||
|
||||
static void VertexFromPlane(MeshType &m, const Plane3<ScalarType> &pl)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() =SignedDistancePlanePoint(pl,(*vi).cP());
|
||||
}
|
||||
|
||||
static void VertexGaussianFromCurvatureDir(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() = (*vi).K1()*(*vi).K2();
|
||||
}
|
||||
|
||||
static void VertexMeanFromCurvatureDir(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() = ((*vi).K1()+(*vi).K2())/2.0f;
|
||||
}
|
||||
static void VertexMinCurvFromCurvatureDir(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() = (*vi).K1();
|
||||
}
|
||||
static void VertexMaxCurvFromCurvatureDir(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() = (*vi).K2();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief VertexShapeIndexFromCurvatureDir
|
||||
* Compute from the current Curvature Direction the Shape Index S as defined by [Koenderink 1992]
|
||||
* and store it in the per-vertex Quality.
|
||||
* S = 2/pi atan(k1+k2/k1-k2)
|
||||
*
|
||||
* J. Koenderink and A. van Doorn.
|
||||
* Surface shape and curvature scales.
|
||||
* Image and vision computing, 10(8):557–565, 1992.
|
||||
*/
|
||||
|
||||
static void VertexShapeIndexFromCurvatureDir(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
{
|
||||
ScalarType k1=(*vi).K1();
|
||||
ScalarType k2=(*vi).K2();
|
||||
if(k1<k2) std::swap(k1,k2);
|
||||
(*vi).Q() = (2.0/M_PI)*atan2(k1+k2,k1-k2);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief VertexCurvednessFromCurvatureDir
|
||||
* Compute from the current Curvature Direction the Curvedness as defined by [Koenderink 1992]
|
||||
* and store it in the per-vertex Quality.
|
||||
* C = Sqrt((k1*k1+k2*k2)/2.0)
|
||||
*
|
||||
* J. Koenderink and A. van Doorn.
|
||||
* Surface shape and curvature scales.
|
||||
* Image and vision computing, 10(8):557–565, 1992.
|
||||
*/
|
||||
static void VertexCurvednessFromCurvatureDir(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequirePerVertexCurvatureDir(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
{
|
||||
const ScalarType k1=(*vi).K1();
|
||||
const ScalarType k2=(*vi).K2();
|
||||
|
||||
(*vi).Q() = math::Sqrt((k1*k1+k2*k2)/2.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Absolute Curvature
|
||||
*
|
||||
* 2|H| if K >= 0
|
||||
* |k1| + |k2| = <
|
||||
* 2 * sqrt(|H|^2-K) otherwise
|
||||
*
|
||||
* defs and formulas taken from
|
||||
*
|
||||
* Improved curvature estimation for watershed segmentation of 3-dimensional meshes
|
||||
* S Pulla, A Razdan, G Farin - Arizona State University, Tech. Rep, 2001
|
||||
* and from
|
||||
* Optimizing 3D triangulations using discrete curvature analysis
|
||||
* N Dyn, K Hormann, SJ Kim, D Levin - Mathematical Methods for Curves and Surfaces: Oslo, 2000
|
||||
*/
|
||||
|
||||
static void VertexAbsoluteCurvatureFromHGAttribute(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
auto KH = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KH"));
|
||||
auto KG = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KG"));
|
||||
for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
{
|
||||
if(KG[vi] >= 0)
|
||||
(*vi).Q() = math::Abs( 2.0*KH[vi] );
|
||||
else
|
||||
(*vi).Q() = 2*math::Sqrt(math::Abs( KH[vi]*KH[vi] - KG[vi]));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RMS Curvature = sqrt(4H^2-2K)
|
||||
* def and formula taken from
|
||||
*
|
||||
* Improved curvature estimation for watershed segmentation of 3-dimensional meshes
|
||||
* S Pulla, A Razdan, G Farin - Arizona State University, Tech. Rep, 2001
|
||||
*/
|
||||
static void VertexRMSCurvatureFromHGAttribute(MeshType &m)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
auto KH = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KH"));
|
||||
auto KG = vcg::tri::Allocator<MeshType>:: template GetPerVertexAttribute<ScalarType> (m, std::string("KG"));
|
||||
for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
(*vi).Q() = math::Sqrt(math::Abs( 4*KH[vi]*KH[vi] - 2*KG[vi]));
|
||||
}
|
||||
|
||||
/*
|
||||
Saturate the Face quality so that for each vertex the gradient of the quality is lower than the given threshold value (in absolute value)
|
||||
The saturation is done in a conservative way (quality is always decreased and never increased)
|
||||
|
||||
Note: requires FF adjacency.
|
||||
*/
|
||||
static void FaceSaturate(MeshType &m, FaceQualityType gradientThr=1.0)
|
||||
{
|
||||
tri::RequirePerFaceQuality(m);
|
||||
tri::RequireFFAdjacency(m);
|
||||
UpdateFlags<MeshType>::FaceClearV(m);
|
||||
std::stack<FacePointer> st;
|
||||
|
||||
st.push(&*m.face.begin());
|
||||
|
||||
while(!st.empty())
|
||||
{
|
||||
FacePointer fc = st.top(); // the center
|
||||
//printf("Stack size %i\n",st.size());
|
||||
//printf("Pop elem %i %f\n",st.top() - &*m.vert.begin(), st.top()->Q());
|
||||
st.pop();
|
||||
fc->SetV();
|
||||
std::vector<FacePointer> star;
|
||||
typename std::vector<FacePointer>::iterator ffi;
|
||||
for (int i=0;i<3;i++)
|
||||
{
|
||||
FacePointer fnext=fc->FFp(i);
|
||||
if (fnext!=fc)star.push_back(fnext);
|
||||
}
|
||||
CoordType bary0=(fc->P(0)+fc->P(1)+fc->P(2))/3;
|
||||
for(ffi=star.begin();ffi!=star.end();++ffi )
|
||||
{
|
||||
assert(fc!=(*ffi));
|
||||
FaceQualityType &qi = (*ffi)->Q();
|
||||
CoordType bary1=((*ffi)->P(0)+(*ffi)->P(1)+(*ffi)->P(2))/3;
|
||||
FaceQualityType distGeom = Distance(bary0,bary1) / gradientThr;
|
||||
// Main test if the quality varies more than the geometric displacement we have to lower something.
|
||||
if( distGeom < fabs(qi - fc->Q()))
|
||||
{
|
||||
// center = 0 other=10 -> other =
|
||||
// center = 10 other=0
|
||||
if(fc->Q() > qi) // first case: the center of the star has to be lowered (and re-inserted in the queue).
|
||||
{
|
||||
//printf("Reinserting center %i \n",vc - &*m.vert.begin());
|
||||
fc->Q() = qi+distGeom-(ScalarType)0.00001;
|
||||
assert( distGeom > fabs(qi - fc->Q()));
|
||||
st.push(fc);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// second case: you have to lower qi, the vertex under examination.
|
||||
assert( distGeom < fabs(qi - fc->Q()));
|
||||
assert(fc->Q() < qi);
|
||||
FaceQualityType newQi = fc->Q() + distGeom -(FaceQualityType)0.00001;
|
||||
assert(newQi <= qi);
|
||||
assert(fc->Q() < newQi);
|
||||
assert( distGeom > fabs(newQi - fc->Q()) );
|
||||
// printf("distGeom %f, qi %f, vc->Q() %f, fabs(qi - vc->Q()) %f\n",distGeom,qi,vc->Q(),fabs(qi - vc->Q()));
|
||||
qi = newQi;
|
||||
(*ffi)->ClearV();
|
||||
}
|
||||
}
|
||||
if(!(*ffi)->IsV())
|
||||
{
|
||||
st.push( *ffi);
|
||||
// printf("Reinserting side %i \n",*vvi - &*m.vert.begin());
|
||||
(*ffi)->SetV();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Saturate Vertex Quality
|
||||
* Saturate the vertex quality so that for each vertex the gradient of the quality field
|
||||
* is lower than the given threshold value (in absolute value)
|
||||
* The saturation is done in a conservative way (quality is always decreased and never increased)
|
||||
|
||||
* Note: requires VF adjacency.
|
||||
*/
|
||||
static void VertexSaturate(MeshType &m, ScalarType gradientThr=1.0)
|
||||
{
|
||||
tri::RequirePerVertexQuality(m);
|
||||
tri::RequireVFAdjacency(m);
|
||||
|
||||
UpdateFlags<MeshType>::VertexClearV(m);
|
||||
std::stack<VertexPointer> st;
|
||||
|
||||
st.push(&*m.vert.begin());
|
||||
|
||||
while(!st.empty())
|
||||
{
|
||||
VertexPointer vc = st.top(); // the center
|
||||
//printf("Stack size %i\n",st.size());
|
||||
//printf("Pop elem %i %f\n",st.top() - &*m.vert.begin(), st.top()->Q());
|
||||
st.pop();
|
||||
vc->SetV();
|
||||
std::vector<VertexPointer> star;
|
||||
face::VVStarVF<FaceType>(vc,star);
|
||||
for(auto vvi=star.begin();vvi!=star.end();++vvi )
|
||||
{
|
||||
ScalarType qi = (*vvi)->Q();
|
||||
ScalarType distGeom = Distance((*vvi)->cP(),vc->cP()) / gradientThr;
|
||||
// Main test if the quality varies more than the geometric displacement we have to lower something.
|
||||
if( distGeom < fabs(qi - vc->Q()))
|
||||
{
|
||||
// center = 0 other=10 -> other =
|
||||
// center = 10 other=0
|
||||
if(vc->Q() > qi) // first case: the center of the star has to be lowered (and re-inserted in the queue).
|
||||
{
|
||||
//printf("Reinserting center %i \n",vc - &*m.vert.begin());
|
||||
VertexQualityType delta=std::min(ScalarType(0.00001),ScalarType(distGeom/2.0));
|
||||
vc->Q() = VertexQualityType(qi+distGeom-delta);
|
||||
assert( distGeom > fabs(qi - vc->Q()));
|
||||
st.push(vc);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// second case: you have to lower qi, the vertex under examination.
|
||||
assert( distGeom < fabs(qi - vc->Q()));
|
||||
assert(vc->Q() < qi);
|
||||
VertexQualityType delta=std::min(VertexQualityType(0.00001),VertexQualityType(distGeom/2.0));
|
||||
VertexQualityType newQi = vc->Q() + distGeom -delta;
|
||||
assert(newQi <= qi);
|
||||
assert(vc->Q() < newQi);
|
||||
assert( distGeom > fabs(newQi - vc->Q()) );
|
||||
// printf("distGeom %f, qi %f, vc->Q() %f, fabs(qi - vc->Q()) %f\n",distGeom,qi,vc->Q(),fabs(qi - vc->Q()));
|
||||
qi = newQi;
|
||||
(*vvi)->ClearV();
|
||||
}
|
||||
}
|
||||
if(!(*vvi)->IsV())
|
||||
{
|
||||
st.push( *vvi);
|
||||
// printf("Reinserting side %i \n",*vvi - &*m.vert.begin());
|
||||
(*vvi)->SetV();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}; //end class
|
||||
} // end namespace
|
||||
} // end namespace
|
||||
#endif
|
||||
@@ -0,0 +1,703 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_SELECTION
|
||||
#define __VCG_TRI_UPDATE_SELECTION
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <vcg/complex/base.h>
|
||||
#include <vcg/simplex/face/topology.h>
|
||||
|
||||
#include "flag.h"
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
/// \ingroup trimesh
|
||||
/// \brief A stack for saving and restoring selection.
|
||||
/**
|
||||
This class is used to save the current selection onto a stack for later use.
|
||||
\todo it should be generalized to other attributes with a templated approach.
|
||||
*/
|
||||
template <class ComputeMeshType>
|
||||
class SelectionStack
|
||||
{
|
||||
typedef typename ComputeMeshType::template PerVertexAttributeHandle< bool > vsHandle;
|
||||
typedef typename ComputeMeshType::template PerEdgeAttributeHandle< bool > esHandle;
|
||||
typedef typename ComputeMeshType::template PerFaceAttributeHandle< bool > fsHandle;
|
||||
typedef typename ComputeMeshType::template PerTetraAttributeHandle< bool > tsHandle;
|
||||
|
||||
|
||||
public:
|
||||
SelectionStack(ComputeMeshType &m)
|
||||
{
|
||||
_m=&m;
|
||||
}
|
||||
|
||||
bool push()
|
||||
{
|
||||
vsHandle vsH = Allocator<ComputeMeshType>::template AddPerVertexAttribute< bool >(*_m);
|
||||
esHandle esH = Allocator<ComputeMeshType>::template AddPerEdgeAttribute< bool > (*_m);
|
||||
fsHandle fsH = Allocator<ComputeMeshType>::template AddPerFaceAttribute< bool > (*_m);
|
||||
tsHandle tsH = Allocator<ComputeMeshType>::template AddPerTetraAttribute< bool > (*_m);
|
||||
typename ComputeMeshType::VertexIterator vi;
|
||||
for(vi = _m->vert.begin(); vi != _m->vert.end(); ++vi)
|
||||
if( !(*vi).IsD() ) vsH[*vi] = (*vi).IsS() ;
|
||||
|
||||
typename ComputeMeshType::EdgeIterator ei;
|
||||
for(ei = _m->edge.begin(); ei != _m->edge.end(); ++ei)
|
||||
if( !(*ei).IsD() ) esH[*ei] = (*ei).IsS() ;
|
||||
|
||||
typename ComputeMeshType::FaceIterator fi;
|
||||
for(fi = _m->face.begin(); fi != _m->face.end(); ++fi)
|
||||
if( !(*fi).IsD() ) fsH[*fi] = (*fi).IsS() ;
|
||||
|
||||
typename ComputeMeshType::TetraIterator ti;
|
||||
for(ti = _m->tetra.begin(); ti != _m->tetra.end(); ++ti)
|
||||
if( !(*ti).IsD() ) tsH[*ti] = (*ti).IsS() ;
|
||||
|
||||
vsV.push_back(vsH);
|
||||
esV.push_back(esH);
|
||||
fsV.push_back(fsH);
|
||||
tsV.push_back(tsH);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool popOr()
|
||||
{
|
||||
return pop(true,false);
|
||||
}
|
||||
|
||||
bool popAnd()
|
||||
{
|
||||
return pop(false,true);
|
||||
}
|
||||
|
||||
/// It restore a saved selection.
|
||||
/// The process can be done or in a straightforward manner (e.g. selection values are substituted)
|
||||
/// or preserving selected or unselected elements (e.g. the restoring is combined in OR/AND)
|
||||
///
|
||||
bool pop(bool orFlag=false, bool andFlag=false)
|
||||
{
|
||||
if(vsV.empty()) return false;
|
||||
if(orFlag && andFlag) return false;
|
||||
|
||||
vsHandle vsH = vsV.back();
|
||||
esHandle esH = esV.back();
|
||||
fsHandle fsH = fsV.back();
|
||||
tsHandle tsH = tsV.back();
|
||||
|
||||
if(! (Allocator<ComputeMeshType>::IsValidHandle(*_m, vsH))) return false;
|
||||
|
||||
for(auto vi = _m->vert.begin(); vi != _m->vert.end(); ++vi)
|
||||
if( !(*vi).IsD() )
|
||||
{
|
||||
if(vsH[*vi]) {
|
||||
if(!andFlag) (*vi).SetS();
|
||||
} else {
|
||||
if(!orFlag) (*vi).ClearS();
|
||||
}
|
||||
}
|
||||
|
||||
for(auto ei = _m->edge.begin(); ei != _m->edge.end(); ++ei)
|
||||
if( !(*ei).IsD() )
|
||||
{
|
||||
if(esH[*ei]) {
|
||||
if(!andFlag) (*ei).SetS();
|
||||
} else {
|
||||
if(!orFlag) (*ei).ClearS();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(auto fi = _m->face.begin(); fi != _m->face.end(); ++fi)
|
||||
if( !(*fi).IsD() )
|
||||
{
|
||||
if(fsH[*fi]) {
|
||||
if(!andFlag) (*fi).SetS();
|
||||
} else {
|
||||
if(!orFlag) (*fi).ClearS();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto ti = _m->tetra.begin(); ti != _m->tetra.end(); ++ti)
|
||||
if (!(*ti).IsD())
|
||||
{
|
||||
if (tsH[*ti]) {
|
||||
if (!andFlag) (*ti).SetS();
|
||||
} else {
|
||||
if (!orFlag) (*ti).ClearS();
|
||||
}
|
||||
}
|
||||
|
||||
Allocator<ComputeMeshType>::template DeletePerVertexAttribute<bool>(*_m,vsH);
|
||||
Allocator<ComputeMeshType>::template DeletePerEdgeAttribute<bool>(*_m,esH);
|
||||
Allocator<ComputeMeshType>::template DeletePerFaceAttribute<bool>(*_m,fsH);
|
||||
Allocator<ComputeMeshType>::template DeletePerTetraAttribute<bool>(*_m,tsH);
|
||||
|
||||
vsV.pop_back();
|
||||
esV.pop_back();
|
||||
fsV.pop_back();
|
||||
tsV.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ComputeMeshType *_m;
|
||||
std::vector<vsHandle> vsV;
|
||||
std::vector<esHandle> esV;
|
||||
std::vector<fsHandle> fsV;
|
||||
std::vector<tsHandle> tsV;
|
||||
|
||||
};
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile selection.h vcg/complex/algorithms/update/selection.h
|
||||
|
||||
/// \brief Management, updating and conditional computation of selections (per-vertex, per-edge, and per-face).
|
||||
/**
|
||||
This class is used to compute or update the selected bit flag that can be stored in the vertex, edge or face component of a mesh.
|
||||
*/
|
||||
|
||||
template <class ComputeMeshType>
|
||||
class UpdateSelection
|
||||
{
|
||||
|
||||
public:
|
||||
typedef ComputeMeshType MeshType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::EdgeIterator EdgeIterator;
|
||||
typedef typename MeshType::EdgeType EdgeType;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::TetraType TetraType;
|
||||
typedef typename MeshType::TetraPointer TetraPointer;
|
||||
typedef typename MeshType::TetraIterator TetraIterator;
|
||||
|
||||
typedef typename vcg::Box3<ScalarType> Box3Type;
|
||||
|
||||
/// \brief This function select all the vertices.
|
||||
static size_t VertexAll(MeshType &m)
|
||||
{
|
||||
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
if( !(*vi).IsD() ) (*vi).SetS();
|
||||
return m.vn;
|
||||
}
|
||||
|
||||
/// \brief This function select all the edges.
|
||||
static size_t EdgeAll(MeshType &m)
|
||||
{
|
||||
for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
|
||||
if( !(*ei).IsD() ) (*ei).SetS();
|
||||
return m.fn;
|
||||
}
|
||||
/// \brief This function select all the faces.
|
||||
static size_t FaceAll(MeshType &m)
|
||||
{
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD() ) (*fi).SetS();
|
||||
return m.fn;
|
||||
}
|
||||
|
||||
/// \brief This function select all the tetras.
|
||||
static size_t TetraAll (MeshType & m)
|
||||
{
|
||||
ForEachTetra(m, [] (TetraType & t) {
|
||||
t.SetS();
|
||||
});
|
||||
|
||||
return m.tn;
|
||||
}
|
||||
|
||||
/// \brief This function clear the selection flag for all the vertices.
|
||||
static size_t VertexClear(MeshType &m)
|
||||
{
|
||||
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
if( !(*vi).IsD() ) (*vi).ClearS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief This function clears the selection flag for all the edges.
|
||||
static size_t EdgeClear(MeshType &m)
|
||||
{
|
||||
for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
|
||||
if( !(*ei).IsD() ) (*ei).ClearS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief This function clears the selection flag for all the faces.
|
||||
static size_t FaceClear(MeshType &m)
|
||||
{
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD() ) (*fi).ClearS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief This function clears the selection flag for all the tetras.
|
||||
static size_t TetraClear (MeshType & m)
|
||||
{
|
||||
ForEachTetra(m, [] (TetraType & t) {
|
||||
t.ClearS();
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief This function clears the selection flag for all the elements of a mesh (vertices, edges, and faces).
|
||||
static void Clear(MeshType &m)
|
||||
{
|
||||
VertexClear(m);
|
||||
EdgeClear(m);
|
||||
FaceClear(m);
|
||||
TetraClear(m);
|
||||
}
|
||||
|
||||
/// \brief This function returns the number of selected faces.
|
||||
static size_t FaceCount(const MeshType &m)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
ForEachFace(m, [&](const FaceType& f){
|
||||
if(f.IsS()) ++selCnt;
|
||||
});
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function returns the number of selected edges.
|
||||
static size_t EdgeCount(const MeshType &m)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
ForEachEdge(m, [&](const EdgeType& e){
|
||||
if(e.IsS()) ++selCnt;
|
||||
});
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function returns the number of selected edges according to the FaceEdge Selection bit (the 3 bits stored inside each face).
|
||||
static size_t FaceEdgeCount(const MeshType &m)
|
||||
{
|
||||
RequireFFAdjacency(m);
|
||||
size_t selCnt=0;
|
||||
ForEachFace(m, [&](const FaceType& f){
|
||||
for(int i=0;i<f.VN();++i)
|
||||
{
|
||||
if(f.IsFaceEdgeS(i)) ++selCnt;
|
||||
if(f.IsFaceEdgeS(i) && face::IsBorder(f,i)) ++selCnt; // all FaceEdges are counted twice with the exception of the ones on borders
|
||||
}
|
||||
});
|
||||
return selCnt/2;
|
||||
}
|
||||
|
||||
/// \brief This function returns the number of selected vertices.
|
||||
static size_t VertexCount(const MeshType &m)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
ForEachVertex(m, [&](const VertexType& v){
|
||||
if(v.IsS()) ++selCnt;
|
||||
});
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function returns the number of selected tetras.
|
||||
static size_t TetraCount (const MeshType & m)
|
||||
{
|
||||
size_t selCnt = 0;
|
||||
ForEachTetra(m, [&] (const TetraType & t) {
|
||||
if (t.IsS()) ++selCnt;
|
||||
});
|
||||
|
||||
return selCnt;
|
||||
}
|
||||
/// \brief This function inverts the selection flag for all the faces.
|
||||
static size_t FaceInvert(MeshType &m)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
if((*fi).IsS()) (*fi).ClearS();
|
||||
else {
|
||||
(*fi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function inverts the selection flag for all the edges.
|
||||
static size_t EdgeInvert(MeshType &m)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
|
||||
if(!(*ei).IsD())
|
||||
{
|
||||
if((*ei).IsS()) (*ei).ClearS();
|
||||
else {
|
||||
(*ei).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function inverts the selection flag for all the vertices.
|
||||
static size_t VertexInvert(MeshType &m)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD())
|
||||
{
|
||||
if((*vi).IsS()) (*vi).ClearS();
|
||||
else {
|
||||
(*vi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function inverts the selection flag for all the tetras.
|
||||
static size_t TetraInvert (MeshType & m)
|
||||
{
|
||||
size_t selCnt = 0;
|
||||
ForEachTetra(m, [&selCnt] (TetraType & t) {
|
||||
if (t.IsS())
|
||||
t.ClearS();
|
||||
else
|
||||
{
|
||||
t.SetS();
|
||||
++selCnt;
|
||||
}
|
||||
});
|
||||
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
|
||||
/// \brief Select all the vertices that are touched by at least a single selected faces
|
||||
static size_t VertexFromFaceLoose(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD() && (*fi).IsS())
|
||||
for(int i = 0; i < (*fi).VN(); ++i)
|
||||
if( !(*fi).V(i)->IsS()) { (*fi).V(i)->SetS(); ++selCnt; }
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief Select all the vertices that are touched by at least a single selected edge
|
||||
static size_t VertexFromEdgeLoose(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
|
||||
if( !(*ei).IsD() && (*ei).IsS())
|
||||
{
|
||||
if( !(*ei).V(0)->IsS()) { (*ei).V(0)->SetS(); ++selCnt; }
|
||||
if( !(*ei).V(1)->IsS()) { (*ei).V(1)->SetS(); ++selCnt; }
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief Select ONLY the vertices that are touched ONLY by selected faces
|
||||
/** In other words this function will select all the vertices having all the faces incident on them selected.
|
||||
*/
|
||||
static size_t VertexFromFaceStrict(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
SelectionStack<MeshType> ss(m);
|
||||
if(preserveSelection) ss.push();
|
||||
VertexFromFaceLoose(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD() && !(*fi).IsS())
|
||||
for(int i = 0; i < (*fi).VN(); ++i)
|
||||
(*fi).V(i)->ClearS();
|
||||
|
||||
if(preserveSelection) ss.popOr();
|
||||
return VertexCount(m);
|
||||
}
|
||||
|
||||
/// \brief Select ONLY the faces with ALL the vertices selected
|
||||
static size_t FaceFromVertexStrict(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
SelectionStack<MeshType> ss(m);
|
||||
if(preserveSelection) ss.push();
|
||||
size_t selCnt=0;
|
||||
FaceClear(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD())
|
||||
{
|
||||
bool selFlag=true;
|
||||
for(int i = 0; i < (*fi).VN(); ++i)
|
||||
if(!(*fi).V(i)->IsS())
|
||||
selFlag =false;
|
||||
if(selFlag)
|
||||
{
|
||||
(*fi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
|
||||
if(preserveSelection) ss.popOr();
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief Select all the faces with at least one selected vertex
|
||||
static size_t FaceFromVertexLoose(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
if(!preserveSelection) FaceClear(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD())
|
||||
{
|
||||
bool selVert=false;
|
||||
for(int i = 0; i < (*fi).VN(); ++i)
|
||||
if((*fi).V(i)->IsS())
|
||||
selVert=true;
|
||||
|
||||
if(selVert) {
|
||||
(*fi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
/// \brief This function dilate the face selection by simply first selecting all the vertices touched by the faces and then all the faces touched by these vertices
|
||||
/// Note: it destroys the vertex selection.
|
||||
static size_t FaceDilate(MeshType &m)
|
||||
{
|
||||
tri::UpdateSelection<MeshType>::VertexFromFaceLoose(m);
|
||||
return tri::UpdateSelection<MeshType>::FaceFromVertexLoose(m);
|
||||
}
|
||||
|
||||
/// \brief This function erode the face selection by simply first selecting only the vertices completely surrounded by face and then the only faces with all the selected vertices
|
||||
/// Note: it destroys the vertex selection.
|
||||
static size_t FaceErode(MeshType &m)
|
||||
{
|
||||
tri::UpdateSelection<MeshType>::VertexFromFaceStrict(m);
|
||||
return tri::UpdateSelection<MeshType>::FaceFromVertexStrict(m);
|
||||
}
|
||||
|
||||
|
||||
/// \brief This function select the vertices with the border flag set
|
||||
static size_t VertexFromBorderFlag(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||
if( !(*vi).IsD() )
|
||||
{
|
||||
if((*vi).IsB() )
|
||||
{
|
||||
(*vi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function select the faces that have an edge with the border flag set.
|
||||
static size_t FaceFromBorderFlag(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
tri::RequireTriangularMesh(m);
|
||||
size_t selCnt=0;
|
||||
if(!preserveSelection) FaceClear(m);
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD() )
|
||||
{
|
||||
bool bordFlag=false;
|
||||
for(int i = 0; i < 3; ++i)
|
||||
if((*fi).IsB(i)) bordFlag=true;
|
||||
if(bordFlag)
|
||||
{
|
||||
(*fi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function select the faces that have an edge outside the given range.
|
||||
/// You can skip the second parameter to choose all the edges smaller than a given lenght
|
||||
static size_t FaceOutOfRangeEdge(MeshType &m, ScalarType MinEdgeThr, ScalarType MaxEdgeThr=(std::numeric_limits<ScalarType>::max)(), bool preserveSelection=false)
|
||||
{
|
||||
if(!preserveSelection) FaceClear(m);
|
||||
size_t selCnt = 0;
|
||||
MinEdgeThr=MinEdgeThr*MinEdgeThr;
|
||||
MaxEdgeThr=MaxEdgeThr*MaxEdgeThr;
|
||||
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
for(int i=0;i<(*fi).VN();++i)
|
||||
{
|
||||
const ScalarType squaredEdge=SquaredDistance((*fi).V0(i)->cP(),(*fi).V1(i)->cP());
|
||||
if((squaredEdge<=MinEdgeThr) || (squaredEdge>=MaxEdgeThr) )
|
||||
{
|
||||
selCnt++;
|
||||
(*fi).SetS();
|
||||
break; // skip the rest of the edges of the tri
|
||||
}
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief This function expand current selection to cover the whole connected component.
|
||||
static size_t FaceConnectedFF(MeshType &m)
|
||||
{
|
||||
// it also assumes that the FF adjacency is well computed.
|
||||
RequireFFAdjacency(m);
|
||||
UpdateFlags<MeshType>::FaceClearV(m);
|
||||
|
||||
std::deque<FacePointer> visitStack;
|
||||
size_t selCnt=0;
|
||||
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if( !(*fi).IsD() && (*fi).IsS() && !(*fi).IsV() )
|
||||
visitStack.push_back(&*fi);
|
||||
|
||||
while(!visitStack.empty())
|
||||
{
|
||||
FacePointer fp = visitStack.front();
|
||||
visitStack.pop_front();
|
||||
assert(!fp->IsV());
|
||||
fp->SetV();
|
||||
for(int i=0;i<fp->VN();++i) {
|
||||
FacePointer ff = fp->FFp(i);
|
||||
if(! ff->IsS())
|
||||
{
|
||||
ff->SetS();
|
||||
++selCnt;
|
||||
visitStack.push_back(ff);
|
||||
assert(!ff->IsV());
|
||||
}
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
/// \brief Select the faces whose quality is in the specified closed interval.
|
||||
static size_t FaceFromQualityRange(MeshType &m,float minq, float maxq, bool preserveSelection=false)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
if(!preserveSelection) FaceClear(m);
|
||||
RequirePerFaceQuality(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
if( (*fi).Q()>=minq && (*fi).Q()<=maxq )
|
||||
{
|
||||
(*fi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief Select the vertices whose quality is in the specified closed interval.
|
||||
static size_t VertexFromQualityRange(MeshType &m,float minq, float maxq, bool preserveSelection=false)
|
||||
{
|
||||
size_t selCnt=0;
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
RequirePerVertexQuality(m);
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
if(!(*vi).IsD())
|
||||
{
|
||||
if( (*vi).Q()>=minq && (*vi).Q()<=maxq )
|
||||
{
|
||||
(*vi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief Select the vertices contained in the specified Box
|
||||
static size_t VertexInBox( MeshType & m, const Box3Type &bb, bool preserveSelection=false)
|
||||
{
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
int selCnt=0;
|
||||
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD())
|
||||
{
|
||||
if(bb.IsIn((*vi).cP()) ) {
|
||||
(*vi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
/// \brief Select the border vertices that form a corner along the border
|
||||
/// with an angle that is below a certain threshold (e.g. with 90 will select all the acute angles)
|
||||
/// It assumes that the Per-Vertex border Flag has been set.
|
||||
static size_t VertexCornerBorder(MeshType &m, ScalarType angleRad, bool preserveSelection=false)
|
||||
{
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
SimpleTempData<typename MeshType::VertContainer, ScalarType > angleSumH(m.vert,0);
|
||||
int selCnt=0;
|
||||
for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
angleSumH[vi]=0;
|
||||
|
||||
for(auto fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
|
||||
{
|
||||
for(int i=0;i<(*fi).VN();++i)
|
||||
angleSumH[fi->V(i)] += face::WedgeAngleRad(*fi,i);
|
||||
}
|
||||
|
||||
for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
|
||||
{
|
||||
if(angleSumH[vi]<angleRad && vi->IsB())
|
||||
{
|
||||
(*vi).SetS();
|
||||
++selCnt;
|
||||
}
|
||||
}
|
||||
return selCnt;
|
||||
}
|
||||
|
||||
|
||||
void VertexNonManifoldEdges(MeshType &m, bool preserveSelection=false)
|
||||
{
|
||||
tri::RequireFFAdjacency(m);
|
||||
|
||||
if(!preserveSelection) VertexClear(m);
|
||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD())
|
||||
{
|
||||
for(int i=0;i<fi->VN();++i)
|
||||
if(!IsManifold(*fi,i)){
|
||||
(*fi).V0(i)->SetS();
|
||||
(*fi).V1(i)->SetS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_TEXTURE
|
||||
#define __VCG_TRI_UPDATE_TEXTURE
|
||||
|
||||
//#include <vcg/space/plane.h>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile texture.h vcg/complex/algorithms/update/texture.h
|
||||
|
||||
/// \brief This class is used to update/generate texcoord position according to various critera.
|
||||
template <class ComputeMeshType>
|
||||
class UpdateTexture
|
||||
{
|
||||
|
||||
public:
|
||||
typedef ComputeMeshType MeshType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename vcg::Point2<ScalarType> UVCoordType;
|
||||
|
||||
static void WedgeTexFromPlane(ComputeMeshType &m, const Point3<ScalarType> &uVec, const Point3<ScalarType> &vVec, bool aspectRatio, ScalarType sideGutter=0.0)
|
||||
{
|
||||
Box2f bb;
|
||||
|
||||
FaceIterator fi;
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
for(int i=0;i<3;++i)
|
||||
{
|
||||
(*fi).WT(i).U()= (*fi).V(i)->cP() * uVec;
|
||||
(*fi).WT(i).V()= (*fi).V(i)->cP() * vVec;
|
||||
bb.Add((*fi).WT(i).P());
|
||||
}
|
||||
}
|
||||
|
||||
ScalarType wideU = bb.max[0]- bb.min[0];
|
||||
ScalarType wideV = bb.max[1]- bb.min[1];
|
||||
|
||||
if (sideGutter>0.0)
|
||||
{
|
||||
ScalarType deltaGutter = std::min(wideU, wideV) * std::min(sideGutter, (ScalarType)0.5);
|
||||
|
||||
bb.max[0] += deltaGutter;
|
||||
bb.min[0] -= deltaGutter;
|
||||
bb.max[1] += deltaGutter;
|
||||
bb.min[1] -= deltaGutter;
|
||||
|
||||
wideU = bb.max[0] - bb.min[0];
|
||||
wideV = bb.max[1] - bb.min[1];
|
||||
}
|
||||
|
||||
if (aspectRatio) {
|
||||
wideU = std::max(wideU, wideV);
|
||||
wideV = wideU;
|
||||
}
|
||||
|
||||
for (fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
if (!(*fi).IsD())
|
||||
{
|
||||
for (int i = 0; i<3; ++i)
|
||||
{
|
||||
(*fi).WT(i).U() = ((*fi).WT(i).U() - bb.min[0]) / wideU;
|
||||
(*fi).WT(i).V() = ((*fi).WT(i).V() - bb.min[1]) / wideV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WedgeTexFromVertexTex(ComputeMeshType &m)
|
||||
{
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD())
|
||||
{
|
||||
for(int i=0;i<fi->VN();++i)
|
||||
{
|
||||
(*fi).WT(i).U() = (*fi).V(i)->T().U();
|
||||
(*fi).WT(i).V() = (*fi).V(i)->T().V();
|
||||
(*fi).WT(i).N() = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Currently texture coords are kept for ALL the triangles of a mesh. The texture id is stored with each face.
|
||||
/// if a given face should not have tex coord it has the default -1 value for texture ID.
|
||||
/// This function will add an new fake texture, add that to the list of textures and change all the -1 id to that value.
|
||||
static void WedgeTexRemoveNull(ComputeMeshType &m, const std::string &texturename)
|
||||
{
|
||||
bool found=false;
|
||||
|
||||
FaceIterator fi;
|
||||
// first loop lets check that there are -1 indexed textured face
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD()) if((*fi).WT(0).N()==-1) found = true;
|
||||
|
||||
if(!found) return;
|
||||
m.textures.push_back(texturename);
|
||||
|
||||
int nullId=m.textures.size()-1;
|
||||
|
||||
for(fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if(!(*fi).IsD()) if((*fi).WT(0).N()==-1)
|
||||
{
|
||||
(*fi).WT(0).N() = nullId;
|
||||
(*fi).WT(1).N() = nullId;
|
||||
(*fi).WT(2).N() = nullId;
|
||||
}
|
||||
|
||||
}
|
||||
/** \brief Merge supposedly wrong texcoords
|
||||
* It can happens that for rounding errors texcoords on different wedges but on the same vertex have different tex coords.
|
||||
* This function merges them according a threshold. It requires initialized VF adjacency.
|
||||
* the default for merging is if two textures dist less than one 16th of texel on a 4k texture...
|
||||
*/
|
||||
|
||||
static int WedgeTexMergeClose(ComputeMeshType &m, ScalarType mergeThr = ScalarType(1.0/65536.0) )
|
||||
{
|
||||
tri::RequireVFAdjacency(m);
|
||||
int mergedCnt=0;
|
||||
ForEachVertex(m, [&](VertexType &v){
|
||||
face::VFIterator<FaceType> vfi(&v);
|
||||
std::vector<UVCoordType> clusterVec;
|
||||
clusterVec.push_back(vfi.F()->WT(vfi.I()).P());
|
||||
++vfi;
|
||||
while(!vfi.End())
|
||||
{
|
||||
UVCoordType cur= vfi.F()->WT(vfi.I()).P();
|
||||
bool merged=false;
|
||||
for(auto p:clusterVec) {
|
||||
if(p!=cur && Distance(p,cur) < mergeThr){
|
||||
vfi.F()->WT(vfi.I()).P()=p;
|
||||
++mergedCnt;
|
||||
merged=true;
|
||||
}
|
||||
}
|
||||
if(!merged)
|
||||
clusterVec.push_back(cur);
|
||||
|
||||
++vfi;
|
||||
}
|
||||
});
|
||||
return mergedCnt;
|
||||
}
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,793 @@
|
||||
/****************************************************************************
|
||||
* 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 __VCG_TRI_UPDATE_TOPOLOGY
|
||||
#define __VCG_TRI_UPDATE_TOPOLOGY
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <vcg/complex/base.h>
|
||||
#include <vcg/simplex/face/topology.h>
|
||||
#include <vcg/simplex/edge/pos.h>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
/// \ingroup trimesh
|
||||
|
||||
/// \headerfile topology.h vcg/complex/algorithms/update/topology.h
|
||||
|
||||
/// \brief Generation of per-vertex and per-face topological information.
|
||||
|
||||
template <class UpdateMeshType>
|
||||
class UpdateTopology
|
||||
{
|
||||
|
||||
public:
|
||||
typedef UpdateMeshType MeshType;
|
||||
typedef typename MeshType::ScalarType ScalarType;
|
||||
typedef typename MeshType::VertexType VertexType;
|
||||
typedef typename MeshType::VertexPointer VertexPointer;
|
||||
typedef typename MeshType::VertexIterator VertexIterator;
|
||||
typedef typename MeshType::EdgeType EdgeType;
|
||||
typedef typename MeshType::EdgePointer EdgePointer;
|
||||
typedef typename MeshType::EdgeIterator EdgeIterator;
|
||||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef typename MeshType::TetraType TetraType;
|
||||
typedef typename MeshType::TetraPointer TetraPointer;
|
||||
typedef typename MeshType::TetraIterator TetraIterator;
|
||||
|
||||
|
||||
/// \headerfile topology.h vcg/complex/algorithms/update/topology.h
|
||||
|
||||
/// \brief Auxiliary data structure for computing tetra tetra adjacency information.
|
||||
/**
|
||||
* It identifies a face, storing three vertex pointers and a tetra pointer where it belongs.
|
||||
*/
|
||||
|
||||
class PFace
|
||||
{
|
||||
public:
|
||||
VertexPointer v[3]; //three ordered vertex pointers, identify a face
|
||||
TetraPointer t; //the pointer to the tetra where this face belongs
|
||||
int z; //index in [0..3] of the face in the tetra
|
||||
bool isBorder;
|
||||
|
||||
PFace () {}
|
||||
PFace (TetraPointer tp, const int nz) { this->Set(tp, nz); }
|
||||
|
||||
void Set (TetraPointer tp /*the tetra pointer*/, const int nz /*the face index*/)
|
||||
{
|
||||
assert (tp != 0);
|
||||
assert (nz >= 0 && nz < 4);
|
||||
|
||||
v[0] = tp->V(Tetra::VofF(nz, 0));
|
||||
v[1] = tp->V(Tetra::VofF(nz, 1));
|
||||
v[2] = tp->V(Tetra::VofF(nz, 2));
|
||||
|
||||
assert(v[0] != v[1] && v[1] != v[2]); //no degenerate faces
|
||||
|
||||
if (v[0] > v[1])
|
||||
std::swap(v[0], v[1]);
|
||||
if (v[1] > v[2])
|
||||
std::swap(v[1], v[2]);
|
||||
if (v[0] > v[1])
|
||||
std::swap(v[0], v[1]);
|
||||
|
||||
t = tp;
|
||||
z = nz;
|
||||
|
||||
|
||||
}
|
||||
|
||||
inline bool operator < (const PFace & pf) const
|
||||
{
|
||||
if (v[0] < pf.v[0])
|
||||
return true;
|
||||
else
|
||||
{
|
||||
if (v[0] > pf.v[0]) return false;
|
||||
|
||||
if (v[1] < pf.v[1])
|
||||
return true;
|
||||
else
|
||||
{
|
||||
if (v[1] > pf.v[1]) return false;
|
||||
|
||||
return (v[2] < pf.v[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator == (const PFace & pf) const
|
||||
{
|
||||
return v[0] == pf.v[0] && v[1] == pf.v[1] && v[2] == pf.v[2];
|
||||
}
|
||||
};
|
||||
|
||||
static void FillFaceVector (MeshType & m, std::vector<PFace> & fvec)
|
||||
{
|
||||
ForEachTetra(m, [&fvec] (TetraType & t) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
fvec.push_back(PFace(&t, i));
|
||||
});
|
||||
}
|
||||
|
||||
static void FillUniqueFaceVector (MeshType & m, std::vector<PFace> & fvec)
|
||||
{
|
||||
FillFaceVector(m, fvec);
|
||||
std::sort(fvec.begin(), fvec.end());
|
||||
typename std::vector<PFace>::iterator newEnd = std::unique(fvec.begin(), fvec.end());
|
||||
}
|
||||
|
||||
/// \brief Auxiliary data structure for computing face face adjacency information.
|
||||
/**
|
||||
It identifies and edge storing two vertex pointer and a face pointer where it belong.
|
||||
*/
|
||||
class PEdge
|
||||
{
|
||||
public:
|
||||
|
||||
VertexPointer v[2]; // the two Vertex pointer are ordered!
|
||||
FacePointer f; // the face where this edge belong
|
||||
int z; // index in [0..2] of the edge of the face
|
||||
bool isBorder;
|
||||
|
||||
PEdge() {}
|
||||
PEdge(FacePointer pf, const int nz) { this->Set(pf,nz); }
|
||||
void Set( FacePointer pf, const int nz )
|
||||
{
|
||||
assert(pf!=0);
|
||||
assert(nz>=0);
|
||||
assert(nz<pf->VN());
|
||||
|
||||
v[0] = pf->V(nz);
|
||||
v[1] = pf->V(pf->Next(nz));
|
||||
assert(v[0] != v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes)
|
||||
|
||||
if( v[0] > v[1] ) std::swap(v[0],v[1]);
|
||||
f = pf;
|
||||
z = nz;
|
||||
}
|
||||
|
||||
inline bool operator < ( const PEdge & pe ) const
|
||||
{
|
||||
if( v[0]<pe.v[0] ) return true;
|
||||
else if( v[0]>pe.v[0] ) return false;
|
||||
else return v[1] < pe.v[1];
|
||||
}
|
||||
|
||||
inline bool operator == ( const PEdge & pe ) const
|
||||
{
|
||||
return v[0]==pe.v[0] && v[1]==pe.v[1];
|
||||
}
|
||||
/// Convert from edge barycentric coord to the face barycentric coord a point on the current edge.
|
||||
/// Face barycentric coordinates are relative to the edge face.
|
||||
inline Point3<ScalarType> EdgeBarycentricToFaceBarycentric(ScalarType u) const
|
||||
{
|
||||
Point3<ScalarType> interp(0,0,0);
|
||||
interp[ this->z ] = u;
|
||||
interp[(this->z+1)%3] = 1.0f-u;
|
||||
return interp;
|
||||
}
|
||||
};
|
||||
|
||||
/// Fill a vector with all the edges of the mesh.
|
||||
/// each edge is stored in the vector the number of times that it appears in the mesh, with the referring face.
|
||||
/// optionally it can skip the faux edges (to retrieve only the real edges of a triangulated polygonal mesh)
|
||||
|
||||
static void FillEdgeVector(MeshType &m, std::vector<PEdge> &edgeVec, bool includeFauxEdge=true)
|
||||
{
|
||||
edgeVec.reserve(m.fn*3);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if( ! (*fi).IsD() )
|
||||
for(int j=0;j<(*fi).VN();++j)
|
||||
if(includeFauxEdge || !(*fi).IsF(j))
|
||||
edgeVec.push_back(PEdge(&*fi,j));
|
||||
}
|
||||
|
||||
static void FillUniqueEdgeVector(MeshType &m, std::vector<PEdge> &edgeVec, bool includeFauxEdge=true, bool computeBorderFlag=false)
|
||||
{
|
||||
FillEdgeVector(m,edgeVec,includeFauxEdge);
|
||||
sort(edgeVec.begin(), edgeVec.end()); // oredering by vertex
|
||||
|
||||
if (computeBorderFlag) {
|
||||
for (size_t i=0; i<edgeVec.size(); i++)
|
||||
edgeVec[ i ].isBorder = true;
|
||||
for (size_t i=1; i<edgeVec.size(); i++) {
|
||||
if (edgeVec[i]==edgeVec[i-1])
|
||||
edgeVec[i].isBorder = edgeVec[i-1].isBorder = false;
|
||||
}
|
||||
}
|
||||
|
||||
typename std::vector< PEdge>::iterator newEnd = std::unique(edgeVec.begin(), edgeVec.end());
|
||||
|
||||
edgeVec.resize(newEnd-edgeVec.begin()); // redundant! remove?
|
||||
}
|
||||
|
||||
static void FillSelectedFaceEdgeVector(MeshType &m, std::vector<PEdge> &edgeVec)
|
||||
{
|
||||
edgeVec.reserve(m.fn*3);
|
||||
ForEachFace(m, [&](FaceType &f){
|
||||
for(int j=0;j<f.VN();++j)
|
||||
if(f.IsFaceEdgeS(j))
|
||||
edgeVec.push_back(PEdge(&f,j));
|
||||
});
|
||||
|
||||
sort(edgeVec.begin(), edgeVec.end()); // oredering by vertex
|
||||
edgeVec.erase(std::unique(edgeVec.begin(), edgeVec.end()),edgeVec.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*! \brief Initialize the edge vector all the edges that can be inferred from current face vector, setting up all the current adjacency relations
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
static void AllocateEdge(MeshType &m)
|
||||
{
|
||||
// Delete all the edges (if any)
|
||||
for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
|
||||
tri::Allocator<MeshType>::DeleteEdge(m,*ei);
|
||||
tri::Allocator<MeshType>::CompactEdgeVector(m);
|
||||
|
||||
// Compute and add edges
|
||||
std::vector<PEdge> Edges;
|
||||
FillUniqueEdgeVector(m,Edges,true,tri::HasPerEdgeFlags(m) );
|
||||
assert(m.edge.empty());
|
||||
tri::Allocator<MeshType>::AddEdges(m,Edges.size());
|
||||
assert(m.edge.size()==Edges.size());
|
||||
|
||||
// Setup adjacency relations
|
||||
if(tri::HasEVAdjacency(m))
|
||||
{
|
||||
for(size_t i=0; i< Edges.size(); ++i)
|
||||
{
|
||||
m.edge[i].V(0) = Edges[i].v[0];
|
||||
m.edge[i].V(1) = Edges[i].v[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (tri::HasPerEdgeFlags(m)){
|
||||
for(size_t i=0; i< Edges.size(); ++i) {
|
||||
if (Edges[i].isBorder) m.edge[i].SetB(); else m.edge[i].ClearB();
|
||||
}
|
||||
}
|
||||
|
||||
if(tri::HasEFAdjacency(m)) // Note it is an unordered relation.
|
||||
{
|
||||
for(size_t i=0; i< Edges.size(); ++i)
|
||||
{
|
||||
std::vector<FacePointer> fpVec;
|
||||
std::vector<int> eiVec;
|
||||
face::EFStarFF(Edges[i].f,Edges[i].z,fpVec,eiVec);
|
||||
m.edge[i].EFp() = Edges[i].f;
|
||||
m.edge[i].EFi() = Edges[i].z;
|
||||
}
|
||||
}
|
||||
|
||||
if(tri::HasFEAdjacency(m))
|
||||
{
|
||||
for(size_t i=0; i< Edges.size(); ++i)
|
||||
{
|
||||
std::vector<FacePointer> fpVec;
|
||||
std::vector<int> eiVec;
|
||||
face::EFStarFF(Edges[i].f,Edges[i].z,fpVec,eiVec);
|
||||
for(size_t j=0;j<fpVec.size();++j)
|
||||
fpVec[j]->FEp(eiVec[j])=&(m.edge[i]);
|
||||
|
||||
// Edges[i].f->FE(Edges[i].z) = &(m.edge[i]);
|
||||
// Connect in loop the non manifold
|
||||
// FaceType* fpit=fp;
|
||||
// int eit=ei;
|
||||
|
||||
// do
|
||||
// {
|
||||
// faceVec.push_back(fpit);
|
||||
// indVed.push_back(eit);
|
||||
// FaceType *new_fpit = fpit->FFp(eit);
|
||||
// int new_eit = fpit->FFi(eit);
|
||||
// fpit=new_fpit;
|
||||
// eit=new_eit;
|
||||
// } while(fpit != fp);
|
||||
|
||||
|
||||
// m.edge[i].EFp() = Edges[i].f;
|
||||
// m.edge[i].EFi() = ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// \brief Clear the tetra-tetra topological relation, setting each involved pointer to null.
|
||||
/// useful when you passed a mesh with tt adjacency to an algorithm that does not use it and chould have messed it
|
||||
static void ClearTetraTetra (MeshType & m)
|
||||
{
|
||||
RequireTTAdjacency(m);
|
||||
ForEachTetra(m, [] (TetraType & t) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
t.TTp(i) = NULL;
|
||||
t.TTi(i) = -1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// \brief Updates the Tetra-Tetra topological relation by allowing to retrieve for each tetra what other tetras share their faces.
|
||||
static void TetraTetra (MeshType & m)
|
||||
{
|
||||
RequireTTAdjacency(m);
|
||||
if (m.tn == 0) return;
|
||||
|
||||
std::vector<PFace> fvec;
|
||||
FillFaceVector(m, fvec);
|
||||
std::sort(fvec.begin(), fvec.end());
|
||||
|
||||
typename std::vector<PFace>::iterator pback, pfront;
|
||||
pback = fvec.begin();
|
||||
pfront = fvec.begin();
|
||||
|
||||
do
|
||||
{
|
||||
if (pfront == fvec.end() || !(*pfront == *pback))
|
||||
{
|
||||
typename std::vector<PFace>::iterator q, q_next;
|
||||
for (q = pback; q < pfront - 1; ++q)
|
||||
{
|
||||
assert((*q).z >= 0);
|
||||
q_next = q;
|
||||
++q_next;
|
||||
assert((*q_next).z >= 0 && (*q_next).z < 4);
|
||||
|
||||
(*q).t->TTp(q->z) = (*q_next).t;
|
||||
(*q).t->TTi(q->z) = (*q_next).z;
|
||||
}
|
||||
|
||||
(*q).t->TTp(q->z) = pback->t;
|
||||
(*q).t->TTi(q->z) = pback->z;
|
||||
pback = pfront;
|
||||
}
|
||||
if (pfront == fvec.end()) break;
|
||||
++pfront;
|
||||
} while (true);
|
||||
}
|
||||
/// \brief Clear the Face-Face topological relation setting each involved pointer to null.
|
||||
/// useful when you passed a mesh with ff adjacency to an algorithm that does not use it and could have messed it.
|
||||
static void ClearFaceFace(MeshType &m)
|
||||
{
|
||||
RequireFFAdjacency(m);
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
{
|
||||
if( ! (*fi).IsD() )
|
||||
{
|
||||
for(int j=0;j<fi->VN();++j)
|
||||
{
|
||||
fi->FFp(j)=0;
|
||||
fi->FFi(j)=-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Update the Face-Face topological relation by allowing to retrieve for each face what other faces shares their edges.
|
||||
static void FaceFace(MeshType &m)
|
||||
{
|
||||
RequireFFAdjacency(m);
|
||||
if( m.fn == 0 ) return;
|
||||
// we use an auxiliary vector of pairs (face,edge index) to sort the edges
|
||||
std::vector<PEdge> e;
|
||||
FillEdgeVector(m,e);
|
||||
sort(e.begin(), e.end());
|
||||
|
||||
auto ps = e.begin();
|
||||
auto pe = e.begin();
|
||||
// scans the sorted vector of edges searching for edges with the same pair of vertices
|
||||
// and connect the corresponding faces
|
||||
do
|
||||
{
|
||||
if( pe==e.end() || !(*pe == *ps) )
|
||||
{
|
||||
typename std::vector<PEdge>::iterator q,q_next;
|
||||
for (q=ps;q<pe-1;++q)
|
||||
{
|
||||
assert((*q).z>=0);
|
||||
//assert((*q).z< 3);
|
||||
q_next = q;
|
||||
++q_next;
|
||||
assert((*q_next).z>=0);
|
||||
assert((*q_next).z< (*q_next).f->VN());
|
||||
(*q).f->FFp(q->z) = (*q_next).f;
|
||||
(*q).f->FFi(q->z) = (*q_next).z;
|
||||
}
|
||||
assert((*q).z>=0);
|
||||
assert((*q).z< (*q).f->VN());
|
||||
(*q).f->FFp((*q).z) = ps->f;
|
||||
(*q).f->FFi((*q).z) = ps->z;
|
||||
ps = pe;
|
||||
}
|
||||
if(pe==e.end()) break;
|
||||
++pe;
|
||||
} while(true);
|
||||
}
|
||||
|
||||
|
||||
/// \brief Update the vertex-tetra topological relation.
|
||||
static void VertexTetra(MeshType & m)
|
||||
{
|
||||
RequireVTAdjacency(m);
|
||||
|
||||
|
||||
ForEachVertex(m, [] (VertexType & v) {
|
||||
v.VTp() = NULL;
|
||||
v.VTi() = 0;
|
||||
});
|
||||
|
||||
ForEachTetra(m, [] (TetraType & t) {
|
||||
//this works like this: the first iteration defines the end of the chain
|
||||
//then it backwards chains everything
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
t.VTp(i) = t.V(i)->VTp();
|
||||
t.VTi(i) = t.V(i)->VTi();
|
||||
t.V(i)->VTp() = &t;
|
||||
t.V(i)->VTi() = i;
|
||||
}
|
||||
});
|
||||
}
|
||||
/// \brief Update the Vertex-Face topological relation.
|
||||
/**
|
||||
The function allows to retrieve for each vertex the list of faces sharing this vertex.
|
||||
After this call all the VF component are initialized. Isolated vertices have a null list of faces.
|
||||
\sa vcg::vertex::VFAdj
|
||||
\sa vcg::face::VFAdj
|
||||
*/
|
||||
|
||||
static void VertexFace(MeshType &m)
|
||||
{
|
||||
RequireVFAdjacency(m);
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
{
|
||||
(*vi).VFp() = 0;
|
||||
(*vi).VFi() = 0; // note that (0,-1) means uninitiazlied while 0,0 is the valid initialized values for isolated vertices.
|
||||
}
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
if( ! (*fi).IsD() )
|
||||
{
|
||||
for(int j=0;j<(*fi).VN();++j)
|
||||
{
|
||||
(*fi).VFp(j) = (*fi).V(j)->VFp();
|
||||
(*fi).VFi(j) = (*fi).V(j)->VFi();
|
||||
(*fi).V(j)->VFp() = &(*fi);
|
||||
(*fi).V(j)->VFi() = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \headerfile topology.h vcg/complex/algorithms/update/topology.h
|
||||
|
||||
/// \brief Auxiliairy data structure for computing face face adjacency information.
|
||||
/**
|
||||
It identifies and edge storing two vertex pointer and a face pointer where it belong.
|
||||
*/
|
||||
|
||||
class PEdgeTex
|
||||
{
|
||||
public:
|
||||
|
||||
typename FaceType::TexCoordType v[2]; // the two TexCoord are ordered!
|
||||
FacePointer f; // the face where this edge belong
|
||||
int z; // index in [0..2] of the edge of the face
|
||||
|
||||
PEdgeTex() {}
|
||||
|
||||
void Set( FacePointer pf, const int nz )
|
||||
{
|
||||
assert(pf!=0);
|
||||
assert(nz>=0);
|
||||
assert(nz<3);
|
||||
|
||||
v[0] = pf->WT(nz);
|
||||
v[1] = pf->WT(pf->Next(nz));
|
||||
assert(v[0] != v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes)
|
||||
|
||||
if( v[1] < v[0] ) std::swap(v[0],v[1]);
|
||||
f = pf;
|
||||
z = nz;
|
||||
}
|
||||
|
||||
inline bool operator < ( const PEdgeTex & pe ) const
|
||||
{
|
||||
if( v[0]<pe.v[0] ) return true;
|
||||
else if( pe.v[0]<v[0] ) return false;
|
||||
else return v[1] < pe.v[1];
|
||||
}
|
||||
inline bool operator == ( const PEdgeTex & pe ) const
|
||||
{
|
||||
return (v[0]==pe.v[0]) && (v[1]==pe.v[1]);
|
||||
}
|
||||
inline bool operator != ( const PEdgeTex & pe ) const
|
||||
{
|
||||
return (v[0]!=pe.v[0]) || (v[1]!=pe.v[1]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// \brief Update the Face-Face topological relation so that it reflects the per-wedge texture connectivity
|
||||
|
||||
/**
|
||||
Using this function two faces are adjacent along the FF relation IFF the two faces have matching texture coords along the involved edge.
|
||||
In other words F1->FFp(i) == F2 iff F1 and F2 have the same tex coords along edge i
|
||||
*/
|
||||
|
||||
static void FaceFaceFromTexCoord(MeshType &m)
|
||||
{
|
||||
RequireFFAdjacency(m);
|
||||
RequirePerFaceWedgeTexCoord(m);
|
||||
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
|
||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||
{
|
||||
if (!(*fi).IsD())
|
||||
{
|
||||
for (int i = 0; i < (*fi).VN(); i++)
|
||||
{
|
||||
if (!vcg::face::IsBorder((*fi), i))
|
||||
{
|
||||
typename MeshType::FacePointer nextFace = (*fi).FFp(i);
|
||||
int nextEdgeIndex = (*fi).FFi(i);
|
||||
bool border = false;
|
||||
if ((*fi).cV(i) == nextFace->cV(nextEdgeIndex))
|
||||
{
|
||||
if ((*fi).WT(i) != nextFace->WT(nextEdgeIndex) || (*fi).WT((*fi).Next(i)) != nextFace->WT(nextFace->Next(nextEdgeIndex)))
|
||||
border = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((*fi).WT(i) != nextFace->WT(nextFace->Next(nextEdgeIndex)) || (*fi).WT((*fi).Next(i)) != nextFace->WT(nextEdgeIndex))
|
||||
border = true;
|
||||
}
|
||||
if (border)
|
||||
vcg::face::FFDetach((*fi), i);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Test correctness of VEtopology
|
||||
static void TestVertexEdge(MeshType &m)
|
||||
{
|
||||
std::vector<int> numVertex(m.vert.size(),0);
|
||||
|
||||
tri::RequireVEAdjacency(m);
|
||||
|
||||
for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
|
||||
{
|
||||
if (!(*ei).IsD())
|
||||
{
|
||||
assert(tri::IsValidPointer(m,ei->V(0)));
|
||||
assert(tri::IsValidPointer(m,ei->V(1)));
|
||||
if(ei->VEp(0)) assert(tri::IsValidPointer(m,ei->VEp(0)));
|
||||
if(ei->VEp(1)) assert(tri::IsValidPointer(m,ei->VEp(1)));
|
||||
numVertex[tri::Index(m,(*ei).V(0))]++;
|
||||
numVertex[tri::Index(m,(*ei).V(1))]++;
|
||||
}
|
||||
}
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
{
|
||||
if (!vi->IsD())
|
||||
{
|
||||
int cnt =0;
|
||||
for(edge::VEIterator<EdgeType> vei(&*vi);!vei.End();++vei)
|
||||
cnt++;
|
||||
assert((numVertex[tri::Index(m,*vi)] == 0) == (vi->VEp()==0) );
|
||||
assert(cnt==numVertex[tri::Index(m,*vi)]);
|
||||
(void)cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \brief Test correctness of VFtopology
|
||||
static void TestVertexFace(MeshType &m)
|
||||
{
|
||||
SimpleTempData<typename MeshType::VertContainer, int > numVertex(m.vert,0);
|
||||
|
||||
assert(tri::HasPerVertexVFAdjacency(m));
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
{
|
||||
if (!(*fi).IsD())
|
||||
{
|
||||
numVertex[(*fi).V0(0)]++;
|
||||
numVertex[(*fi).V1(0)]++;
|
||||
numVertex[(*fi).V2(0)]++;
|
||||
}
|
||||
}
|
||||
|
||||
vcg::face::VFIterator<FaceType> VFi;
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
{
|
||||
if (!vi->IsD())
|
||||
if(vi->VFp()!=0) // unreferenced vertices MUST have VF == 0;
|
||||
{
|
||||
int num=0;
|
||||
assert(tri::IsValidPointer(m, vi->VFp()));
|
||||
VFi.f=vi->VFp();
|
||||
VFi.z=vi->VFi();
|
||||
while (!VFi.End())
|
||||
{
|
||||
num++;
|
||||
assert(!VFi.F()->IsD());
|
||||
assert((VFi.F()->V(VFi.I()))==&(*vi));
|
||||
++VFi;
|
||||
}
|
||||
assert(num==numVertex[&(*vi)]);
|
||||
(void)num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Test correctness of FFtopology (only for 2Manifold Meshes!)
|
||||
static void TestFaceFace(MeshType &m)
|
||||
{
|
||||
assert(HasFFAdjacency(m));
|
||||
|
||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||
{
|
||||
if (!fi->IsD())
|
||||
{
|
||||
for (int i=0;i<(*fi).VN();i++)
|
||||
{
|
||||
FaceType *ffpi=fi->FFp(i);
|
||||
int e=fi->FFi(i);
|
||||
//invariant property of FF topology for two manifold meshes
|
||||
assert(ffpi->FFp(e) == &(*fi));
|
||||
assert(ffpi->FFi(e) == i);
|
||||
|
||||
// Test that the two faces shares the same edge
|
||||
// Vertices of the i-th edges of the first face
|
||||
VertexPointer v0i= fi->V0(i);
|
||||
VertexPointer v1i= fi->V1(i);
|
||||
// Vertices of the corresponding edge on the other face
|
||||
VertexPointer ffv0i= ffpi->V0(e);
|
||||
VertexPointer ffv1i= ffpi->V1(e);
|
||||
|
||||
assert( (ffv0i==v0i) || (ffv0i==v1i) );
|
||||
assert( (ffv1i==v0i) || (ffv1i==v1i) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliairy data structure for computing edge edge adjacency information.
|
||||
/// It identifies an edge storing a vertex pointer and a edge pointer where it belong.
|
||||
class PVertexEdge
|
||||
{
|
||||
public:
|
||||
|
||||
VertexPointer v; // the two Vertex pointer are ordered!
|
||||
EdgePointer e; // the edge where this vertex belong
|
||||
int z; // index in [0..1] of the vertex of the edge
|
||||
|
||||
PVertexEdge( ) {}
|
||||
PVertexEdge( EdgePointer pe, const int nz )
|
||||
{
|
||||
assert(pe!=0);
|
||||
assert(nz>=0);
|
||||
assert(nz<2);
|
||||
|
||||
v= pe->V(nz);
|
||||
e = pe;
|
||||
z = nz;
|
||||
}
|
||||
inline bool operator < ( const PVertexEdge & pe ) const { return ( v<pe.v ); }
|
||||
inline bool operator == ( const PVertexEdge & pe ) const { return ( v==pe.v ); }
|
||||
inline bool operator != ( const PVertexEdge & pe ) const { return ( v!=pe.v ); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void EdgeEdge(MeshType &m)
|
||||
{
|
||||
RequireEEAdjacency(m);
|
||||
std::vector<PVertexEdge> v;
|
||||
if( m.en == 0 ) return;
|
||||
|
||||
for(EdgeIterator pf=m.edge.begin(); pf!=m.edge.end(); ++pf)
|
||||
if( ! (*pf).IsD() )
|
||||
for(int j=0;j<2;++j)
|
||||
{
|
||||
v.push_back(PVertexEdge(&*pf,j));
|
||||
}
|
||||
|
||||
sort(v.begin(), v.end());
|
||||
|
||||
auto ps = v.begin();
|
||||
auto pe = v.begin();
|
||||
do
|
||||
{
|
||||
if( pe==v.end() || !(*pe == *ps) ) // search for blocs of edges with the same vertex
|
||||
{
|
||||
typename std::vector<PVertexEdge>::iterator q,q_next;
|
||||
for (q=ps;q<pe-1;++q)
|
||||
{
|
||||
assert((*q).z>=0);
|
||||
assert((*q).z< 2);
|
||||
q_next = q;
|
||||
++q_next;
|
||||
assert((*q_next).z>=0);
|
||||
assert((*q_next).z< 2);
|
||||
(*q).e->EEp(q->z) = (*q_next).e;
|
||||
(*q).e->EEi(q->z) = (*q_next).z;
|
||||
}
|
||||
assert((*q).z>=0);
|
||||
assert((*q).z< 2);
|
||||
(*q).e->EEp((*q).z) = ps->e;
|
||||
(*q).e->EEi((*q).z) = ps->z;
|
||||
ps = pe;
|
||||
}
|
||||
if(pe==v.end()) break;
|
||||
++pe;
|
||||
} while(true);
|
||||
}
|
||||
|
||||
static void VertexEdge(MeshType &m)
|
||||
{
|
||||
RequireVEAdjacency(m);
|
||||
|
||||
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
|
||||
{
|
||||
(*vi).VEp() = 0;
|
||||
(*vi).VEi() = 0;
|
||||
}
|
||||
|
||||
for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
|
||||
if( ! (*ei).IsD() )
|
||||
{
|
||||
for(int j=0;j<2;++j)
|
||||
{ assert(tri::IsValidPointer(m,ei->V(j)));
|
||||
(*ei).VEp(j) = (*ei).V(j)->VEp();
|
||||
(*ei).VEi(j) = (*ei).V(j)->VEi();
|
||||
(*ei).V(j)->VEp() = &(*ei);
|
||||
(*ei).V(j)->VEi() = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // end class
|
||||
|
||||
} // End namespace
|
||||
} // End namespace
|
||||
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user