diff --git a/vcg/complex/algorithms/smooth.h b/vcg/complex/algorithms/smooth.h index e0dbf468..dcd3799f 100644 --- a/vcg/complex/algorithms/smooth.h +++ b/vcg/complex/algorithms/smooth.h @@ -217,26 +217,25 @@ class Smooth //if we are applying to a tetrahedral mesh: ForEachTetra(m, [&](TetraType &t) { - for (int i = 0; i < 4; ++i) - if (!t.IsB(i)) - { - VertexPointer v0, v1, v2; - v0 = t.V(Tetra::VofF(i, 0)); - v1 = t.V(Tetra::VofF(i, 1)); - v2 = t.V(Tetra::VofF(i, 2)); + for (int i = 0; i < 6; ++i) + { + VertexPointer v0, v1, vo0, vo1; + v0 = t.V(Tetra::VofE(i, 0)); + v1 = t.V(Tetra::VofE(i, 1)); - TD[v0].sum += v1->P() * weight; - TD[v0].sum += v2->P() * weight; - TD[v0].cnt += 2 * weight; + vo0 = t.V(Tetra::VofE(5 - i, 0)); + vo1 = t.V(Tetra::VofE(5 - i, 1)); - TD[v1].sum += v0->P() * weight; - TD[v1].sum += v2->P() * weight; - TD[v1].cnt += 2 * weight; + ScalarType angle = Tetra::DihedralAngle(t, 5 - i); + ScalarType length = vcg::Distance(vo0->P(), vo1->P()); - TD[v2].sum += v0->P() * weight; - TD[v2].sum += v1->P() * weight; - TD[v2].cnt += 2 * weight; - } + weight = (length / 6.) * (tan(M_PI / 2. - angle)); + + TD[v0].sum += v1->cP() * weight; + TD[v1].sum += v0->cP() * weight; + TD[v0].cnt += weight; + TD[v1].cnt += weight; + } }); ForEachTetra(m, [&](TetraType &t) { @@ -258,28 +257,28 @@ class Smooth } }); - ForEachTetra(m, [&](TetraType &t) { - for (int i = 0; i < 4; ++i) - if (t.IsB(i)) - { - VertexPointer v0, v1, v2; - v0 = t.V(Tetra::VofF(i, 0)); - v1 = t.V(Tetra::VofF(i, 1)); - v2 = t.V(Tetra::VofF(i, 2)); +// ForEachTetra(m, [&](TetraType &t) { +// for (int i = 0; i < 4; ++i) +// if (t.IsB(i)) +// { +// VertexPointer v0, v1, v2; +// v0 = t.V(Tetra::VofF(i, 0)); +// v1 = t.V(Tetra::VofF(i, 1)); +// v2 = t.V(Tetra::VofF(i, 2)); - TD[v0].sum += v1->P() * weight; - TD[v0].sum += v2->P() * weight; - TD[v0].cnt += 2 * weight; +// TD[v0].sum += v1->P(); +// TD[v0].sum += v2->P(); +// TD[v0].cnt += 2; - TD[v1].sum += v0->P() * weight; - TD[v1].sum += v2->P() * weight; - TD[v1].cnt += 2 * weight; +// TD[v1].sum += v0->P(); +// TD[v1].sum += v2->P(); +// TD[v1].cnt += 2; - TD[v2].sum += v0->P() * weight; - TD[v2].sum += v1->P() * weight; - TD[v2].cnt += 2 * weight; - } - }); +// TD[v2].sum += v0->P(); +// TD[v2].sum += v1->P(); +// TD[v2].cnt += 2; +// } +// }); FaceIterator fi; for (fi = m.face.begin(); fi != m.face.end(); ++fi) diff --git a/vcg/complex/algorithms/tetra_implicit_smooth.h b/vcg/complex/algorithms/tetra_implicit_smooth.h new file mode 100644 index 00000000..49b40a10 --- /dev/null +++ b/vcg/complex/algorithms/tetra_implicit_smooth.h @@ -0,0 +1,495 @@ +/**************************************************************************** +* 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_IMPLICIT_TETRA_SMOOTHER +#define __VCG_IMPLICIT_TETRA_SMOOTHER + +#include +#include +#include +#include + +#define PENALTY 10000 + +namespace vcg +{ + +template +class ImplicitTetraSmoother +{ + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::TetraType TetraType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename Eigen::Matrix MatrixXm; + + public: + struct FaceConstraint + { + int numF; + std::vector BarycentricW; + CoordType TargetPos; + + FaceConstraint() + { + numF = -1; + } + + FaceConstraint(int _numF, + const std::vector &_BarycentricW, + const CoordType &_TargetPos) + { + numF = _numF; + BarycentricW = std::vector(_BarycentricW.begin(), _BarycentricW.end()); + TargetPos = _TargetPos; + } + }; + + struct Parameter + { + //the amount of smoothness, useful only if we set the mass matrix + ScalarType lambda; + //the use of mass matrix to keep the mesh close to its original position + //(weighted per area distributed on vertices) + bool useMassMatrix; + //this bool is used to fix the border vertices of the mesh or not + bool fixBorder; + //this bool is used to set if cotangent weight is used, this flag to false means uniform laplacian + bool useCotWeight; + //use this weight for the laplacian when the cotangent one is not used + ScalarType lapWeight; + //the set of fixed vertices + std::vector FixedV; + //the set of faces for barycentric constraints + std::vector ConstrainedF; + //the degree of laplacian + int degree; + //this is to say if we smooth the positions or the quality + bool SmoothQ; + + Parameter() + { + degree = 2; + lambda = 0.05; + useMassMatrix = true; + fixBorder = true; + useCotWeight = false; + lapWeight = 1; + SmoothQ = false; + } + }; + + private: + static void InitSparse(const std::vector> &Index, + const std::vector &Values, + const int m, + const int n, + Eigen::SparseMatrix &X) + { + assert(Index.size() == Values.size()); + + std::vector> IJV; + IJV.reserve(Index.size()); + + for (size_t i = 0; i < Index.size(); i++) + { + int row = Index[i].first; + int col = Index[i].second; + ScalarType val = Values[i]; + + assert(row < m); + assert(col < n); + + IJV.push_back(Eigen::Triplet(row, col, val)); + } + X.resize(m, n); + X.setFromTriplets(IJV.begin(), IJV.end()); + } + + static void CollectHardConstraints(MeshType &mesh, const Parameter &SParam, + std::vector> &IndexC, + std::vector &WeightC, + bool SmoothQ = false) + { + std::vector To_Fix; + + //collect fixed vert + if (SParam.fixBorder) + { + //add penalization constra + for (size_t i = 0; i < mesh.vert.size(); i++) + { + if (!mesh.vert[i].IsB()) + continue; + To_Fix.push_back(i); + } + } + //add additional fixed vertices constraint + To_Fix.insert(To_Fix.end(), SParam.FixedV.begin(), SParam.FixedV.end()); + + //sort and make them unique + std::sort(To_Fix.begin(), To_Fix.end()); + typename std::vector::iterator it = std::unique(To_Fix.begin(), To_Fix.end()); + To_Fix.resize(std::distance(To_Fix.begin(), it)); + + for (size_t i = 0; i < To_Fix.size(); i++) + { + if (!SmoothQ) + { + for (int j = 0; j < 3; j++) + { + int IndexV = (To_Fix[i] * 3) + j; + IndexC.push_back(std::pair(IndexV, IndexV)); + WeightC.push_back((ScalarType)PENALTY); + } + } + else + { + int IndexV = To_Fix[i]; + IndexC.push_back(std::pair(IndexV, IndexV)); + WeightC.push_back((ScalarType)PENALTY); + } + } + } + + static void CollectBarycentricConstraints(MeshType &mesh, + const Parameter &SParam, + std::vector> &IndexC, + std::vector &WeightC, + std::vector &IndexRhs, + std::vector &ValueRhs) + { + ScalarType penalty; + int baseIndex = mesh.vert.size(); + for (size_t i = 0; i < SParam.ConstrainedF.size(); i++) + { + //get the index of the current constraint + int IndexConstraint = baseIndex + i; + + //add one hard constraint + int FaceN = SParam.ConstrainedF[i].numF; + assert(FaceN >= 0); + assert(FaceN < (int)mesh.face.size()); + assert(mesh.face[FaceN].VN() == (int)SParam.ConstrainedF[i].BarycentricW.size()); + penalty = ScalarType(1) - SParam.lapWeight; + assert(penalty > ScalarType(0) && penalty < ScalarType(1)); + + //then add all the weights to impose the constraint + for (int j = 0; j < mesh.face[FaceN].VN(); j++) + { + //get the current weight + ScalarType currW = SParam.ConstrainedF[i].BarycentricW[j]; + + //get the index of the current vertex + int FaceVert = vcg::tri::Index(mesh, mesh.face[FaceN].V(j)); + + //then add the constraints componentwise + for (int k = 0; k < 3; k++) + { + //multiply times 3 per component + int IndexV = (FaceVert * 3) + k; + + //get the index of the current constraint + int ComponentConstraint = (IndexConstraint * 3) + k; + IndexC.push_back(std::pair(ComponentConstraint, IndexV)); + + WeightC.push_back(currW * penalty); + + IndexC.push_back(std::pair(IndexV, ComponentConstraint)); + WeightC.push_back(currW * penalty); + + //this to avoid the 1 on diagonal last entry of mass matrix + IndexC.push_back(std::pair(ComponentConstraint, ComponentConstraint)); + WeightC.push_back(-1); + } + } + + for (int j = 0; j < 3; j++) + { + //get the index of the current constraint + int ComponentConstraint = (IndexConstraint * 3) + j; + + //get per component value + ScalarType ComponentV = SParam.ConstrainedF[i].TargetPos.V(j); + + //add the diagonal value + IndexRhs.push_back(ComponentConstraint); + ValueRhs.push_back(ComponentV * penalty); + } + } + } + + static void MassMatrixEntry(MeshType &m, + std::vector> &index, + std::vector &entry, + bool vertexCoord = true) + { + tri::RequireCompactness(m); + + typename MeshType::template PerVertexAttributeHandle h = + tri::Allocator::template GetPerVertexAttribute(m, "volume"); + for (int i = 0; i < m.vn; ++i) + h[i] = 0; + + ForEachTetra(m, [&](TetraType &t) { + ScalarType v = Tetra::ComputeVolume(t); + for (int i = 0; i < 4; ++i) + h[tri::Index(m, t.V(i))] += v; + }); + + ScalarType maxV = 0; + for (int i = 0; i < m.vn; ++i) + maxV = max(maxV, h[i]); + + for (int i = 0; i < m.vn; ++i) + { + int currI = i; + index.push_back(std::pair(currI, currI)); + entry.push_back(h[i] / maxV); + } + + tri::Allocator::template DeletePerVertexAttribute(m, h); + } + + static ScalarType ComputeCotangentWeight(TetraType &t, const int i) + { + //i is the edge in the tetra + tetra::Pos pp(&t, Tetra::FofE(i, 0), i, Tetra::VofE(i, 0)); + tetra::Pos pt(&t, Tetra::FofE(i, 0), i, Tetra::VofE(i, 0)); + + ScalarType weight = 0; + + do + { + CoordType po0 = t.V(Tetra::VofE(5 - pt.E(), 0))->cP(); + CoordType po1 = t.V(Tetra::VofE(5 - pt.E(), 1))->cP(); + + ScalarType length = vcg::Distance(po0, po1); + ScalarType cot = std::tan((M_PI / 2.) - Tetra::DihedralAngle(*pt.T(), 5 - pt.E())); + + weight = (length / 6.) * cot; + pt.FlipT(); + pt.FlipF(); + } while (pp != pt); + + return weight; + } + + static void GetLaplacianEntry(MeshType &mesh, + TetraType &t, + std::vector> &index, + std::vector &entry, + bool cotangent, + ScalarType weight = 1, + bool vertexCoord = true) + { + // if (cotangent) + // vcg::tri::MeshAssert::OnlyT(mesh); + //iterate on edges + for (int i = 0; i < 6; ++i) + { + weight = 1;//ComputeCotangentWeight(t, i); + + int indexV0 = Index(mesh, t.V(Tetra::VofE(i, 0))); + int indexV1 = Index(mesh, t.V(Tetra::VofE(i, 1))); + + for (int j = 0; j < 3; j++) + { + //multiply by 3 and add the component + int currI0 = (indexV0 * 3) + j; + int currI1 = (indexV1 * 3) + j; + + index.push_back(std::pair(currI0, currI0)); + entry.push_back(weight); + index.push_back(std::pair(currI0, currI1)); + entry.push_back(-weight); + + index.push_back(std::pair(currI1, currI1)); + entry.push_back(weight); + index.push_back(std::pair(currI1, currI0)); + entry.push_back(-weight); + } + } + } + + static void GetLaplacianMatrix(MeshType &mesh, + std::vector> &index, + std::vector &entry, + bool cotangent, + ScalarType weight = 1, + bool vertexCoord = true) + { + //store the index and the scalar for the sparse matrix + ForEachTetra(mesh, [&](TetraType &t) { + GetLaplacianEntry(mesh, t, index, entry, cotangent, weight); + }); + } + + public: + static void Compute(MeshType &mesh, Parameter &SParam) + { + //calculate the size of the system + int matr_size = mesh.vert.size() + SParam.ConstrainedF.size(); + + //the laplacian and the mass matrix + Eigen::SparseMatrix L, M, B; + + //initialize the mass matrix + std::vector> IndexM; + std::vector ValuesM; + + //add the entries for mass matrix + if (SParam.useMassMatrix) + MassMatrixEntry(mesh, IndexM, ValuesM, !SParam.SmoothQ); + + //then add entries for lagrange mult due to barycentric constraints + for (size_t i = 0; i < SParam.ConstrainedF.size(); i++) + { + int baseIndex = (mesh.vert.size() + i) * 3; + + if (SParam.SmoothQ) + baseIndex = (mesh.vert.size() + i); + + if (SParam.SmoothQ) + { + IndexM.push_back(std::pair(baseIndex, baseIndex)); + ValuesM.push_back(1); + } + else + { + for (int j = 0; j < 3; j++) + { + IndexM.push_back(std::pair(baseIndex + j, baseIndex + j)); + ValuesM.push_back(1); + } + } + } + //add the hard constraints + CollectHardConstraints(mesh, SParam, IndexM, ValuesM, SParam.SmoothQ); + + //initialize sparse mass matrix + if (!SParam.SmoothQ) + InitSparse(IndexM, ValuesM, matr_size * 3, matr_size * 3, M); + else + InitSparse(IndexM, ValuesM, matr_size, matr_size, M); + + //initialize the barycentric matrix + std::vector> IndexB; + std::vector ValuesB; + + std::vector IndexRhs; + std::vector ValuesRhs; + + //then also collect hard constraints + if (!SParam.SmoothQ) + { + CollectBarycentricConstraints(mesh, SParam, IndexB, ValuesB, IndexRhs, ValuesRhs); + //initialize sparse constraint matrix + InitSparse(IndexB, ValuesB, matr_size * 3, matr_size * 3, B); + } + else + InitSparse(IndexB, ValuesB, matr_size, matr_size, B); + + //get the entries for laplacian matrix + std::vector> IndexL; + std::vector ValuesL; + GetLaplacianMatrix(mesh, IndexL, ValuesL, SParam.useCotWeight, SParam.lapWeight, !SParam.SmoothQ); + + //initialize sparse laplacian matrix + if (!SParam.SmoothQ) + InitSparse(IndexL, ValuesL, matr_size * 3, matr_size * 3, L); + else + InitSparse(IndexL, ValuesL, matr_size, matr_size, L); + + for (int i = 0; i < (SParam.degree - 1); i++) + L = L * L; + + //then solve the system + Eigen::SparseMatrix S = (M + B + SParam.lambda * L); + + //SimplicialLDLT + Eigen::SimplicialCholesky> solver(S); + assert(solver.info() == Eigen::Success); + + MatrixXm V; + if (!SParam.SmoothQ) + V = MatrixXm(matr_size * 3, 1); + else + V = MatrixXm(matr_size, 1); + + //set the first part of the matrix with vertex values + if (!SParam.SmoothQ) + { + for (size_t i = 0; i < mesh.vert.size(); i++) + { + int index = i * 3; + V(index, 0) = mesh.vert[i].P().X(); + V(index + 1, 0) = mesh.vert[i].P().Y(); + V(index + 2, 0) = mesh.vert[i].P().Z(); + } + } + else + { + for (size_t i = 0; i < mesh.vert.size(); i++) + { + int index = i; + V(index, 0) = mesh.vert[i].Q(); + } + } + + //then set the second part by considering RHS gien by barycentric constraint + for (size_t i = 0; i < IndexRhs.size(); i++) + { + int index = IndexRhs[i]; + ScalarType val = ValuesRhs[i]; + V(index, 0) = val; + } + + //solve the system + V = solver.solve(M * V).eval(); + + //then copy back values + if (!SParam.SmoothQ) + { + for (size_t i = 0; i < mesh.vert.size(); i++) + { + int index = i * 3; + mesh.vert[i].P().X() = V(index, 0); + mesh.vert[i].P().Y() = V(index + 1, 0); + mesh.vert[i].P().Z() = V(index + 2, 0); + } + } + else + { + for (size_t i = 0; i < mesh.vert.size(); i++) + { + int index = i; + mesh.vert[i].Q() = V(index, 0); + } + } + } +}; + +} //end namespace vcg + +#endif diff --git a/vcg/complex/algorithms/update/flag.h b/vcg/complex/algorithms/update/flag.h index 1ff86976..c4d74ac4 100644 --- a/vcg/complex/algorithms/update/flag.h +++ b/vcg/complex/algorithms/update/flag.h @@ -40,466 +40,486 @@ 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; + 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) + /// \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 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 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 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 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 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 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 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 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 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 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. - /** + 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); + 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;jVN();++j) - { - if(face::IsBorder(*fi,j)) (*fi).SetB(j); - else (*fi).ClearB(j); - } - } + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD()) + for(int j=0;jVN();++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. - /** + /// \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 (tetrahedron::IsBorder(*ti,j)) (*ti).SetB(j); - else (*ti).ClearB(j); - } - } - - 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 vfi(&*vi) ; !vfi.End(); ++vfi ) - { - vfi.f->V1(vfi.z)->ClearUserBit(visitedBit); - vfi.f->V2(vfi.z)->ClearUserBit(visitedBit); - } - for(face::VFIterator 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 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 ) + static void TetraBorderFromTT(MeshType &m) { - assert(pf!=0); - assert(nz>=0); - assert(nz<3); + RequirePerTetraFlags(m); + RequireTTAdjacency(m); - 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; + for(TetraIterator ti=m.tetra.begin(); ti!=m.tetra.end(); ++ti) + if(!(*ti).IsD()) + for(int j = 0; j < 4; ++j) + { + if (tetrahedron::IsBorder(*ti,j)) (*ti).SetB(j); + else (*ti).ClearB(j); + } } - inline bool operator < ( const EdgeSorter & pe ) const { - if( v[0]pe.v[0] ) return false; - else return v[1] < pe.v[1]; - } - - inline bool operator == ( const EdgeSorter & pe ) const + static void VertexBorderFromTT(MeshType &m) { - return v[0]==pe.v[0] && v[1]==pe.v[1]; + 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 (tetrahedron::IsBorder(*ti,j)) + { + for (int i = 0; i < 3; ++i) + ti->V(Tetra::VofF(j, i))->SetB(); + } + } } - inline bool operator != ( const EdgeSorter & pe ) const + + + static void FaceBorderFromVF(MeshType &m) { - return v[0]!=pe.v[0] || v[1]!=pe.v[1]; + 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 vfi(&*vi) ; !vfi.End(); ++vfi ) + { + vfi.f->V1(vfi.z)->ClearUserBit(visitedBit); + vfi.f->V2(vfi.z)->ClearUserBit(visitedBit); + } + for(face::VFIterator 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 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 - // versione minimale che non calcola i complex flag. - static void VertexBorderFromNone(MeshType &m) - { - RequirePerVertexFlags(m); - - std::vector e; - typename UpdateMeshType::FaceIterator pf; - typename std::vector::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) + void Set( const FacePointer pf, const int nz ) { - (*p).Set(&(*pf),j); - (*pf).ClearB(j); - ++p; - } - assert(p==e.end()); - sort(e.begin(), e.end()); // Lo ordino per vertici + assert(pf!=0); + assert(nz>=0); + assert(nz<3); - typename std::vector::iterator pe,ps; - for(ps = e.begin(), pe = e.begin(); pe < e.end(); ++pe) // Scansione vettore ausiliario + 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 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) { - if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali - { - if(pe-ps==1) { - ps->v[0]->SetB(); - ps->v[1]->SetB(); - }/* else + RequirePerVertexFlags(m); + + std::vector e; + typename UpdateMeshType::FaceIterator pf; + typename std::vector::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::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 e; - typename UpdateMeshType::FaceIterator pf; - typename std::vector::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; + ps = pe; + } } - assert(p==e.end()); - sort(e.begin(), e.end()); // Lo ordino per vertici + } - typename std::vector::iterator pe,ps; - ps = e.begin();pe=e.begin(); - do + /// Computes per-face border flags without requiring any kind of topology + /// It has a O(fn log fn) complexity. + static void FaceBorderFromNone(MeshType &m) { - if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali - { - if(pe-ps==1) { - ps->f->SetB(ps->z); - } /*else + RequirePerFaceFlags(m); + + std::vector e; + typename UpdateMeshType::FaceIterator pf; + typename std::vector::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::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::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 faux (e.g all crease) - FaceClearFaceEdgeS(m); - // Then mark faux only if the signed angle is 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(angleAngleRadPos) - (*fi).SetFaceEdgeS(z); - } - else - { - if(MarkBorderFlag) (*fi).SetFaceEdgeS(z); - } - } + ps = pe; + } + if(pe==e.end()) break; + ++pe; + } while(true); + // TRACE("found %i border (%i complex) on %i edges\n",nborder,ncomplex,ne); } - } - /// \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); - } - } - } + /// Compute the PerVertex Border flag deriving it from the face-face adjacency + static void VertexBorderFromFaceAdj(MeshType &m) + { + RequirePerFaceFlags(m); + RequirePerVertexFlags(m); + RequireFFAdjacency(m); + // MeshAssert::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 faux (e.g all crease) + FaceClearFaceEdgeS(m); + // Then mark faux only if the signed angle is 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(angleAngleRadPos) + (*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); + } - /// \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 diff --git a/vcg/simplex/tetrahedron/pos.h b/vcg/simplex/tetrahedron/pos.h index 216c811e..59b35ae7 100644 --- a/vcg/simplex/tetrahedron/pos.h +++ b/vcg/simplex/tetrahedron/pos.h @@ -165,7 +165,7 @@ public: return _e; } - /// Return the index of face as seen from the tetrahedron + /// Return the index of edge as seen from the tetrahedron inline const char & E() const { return _e; @@ -269,7 +269,7 @@ public: //get the current vertex VertexType *vcurr=T()->V(V()); - //get new tetrahedron according to faceto face topology + //get new tetrahedron according to tetra to tetra topology TetraType *nt=T()->TTp(F()); char nfa=T()->TTi(F()); if (nfa!=-1)