// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2005 Imetric 3D GmbH * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include #include "Degeneration.h" #include "Grid.h" #include "Iterator.h" #include "TopoAlgorithm.h" #include "Triangulation.h" using namespace MeshCore; bool MeshEvalInvalids::Evaluate() { const MeshFacetArray& rFaces = _rclMesh.GetFacets(); for (const auto& it : rFaces) { if (!it.IsValid()) { return false; } } const MeshPointArray& rPoints = _rclMesh.GetPoints(); for (const auto& it : rPoints) { if (!it.IsValid()) { return false; } } return true; } std::vector MeshEvalInvalids::GetIndices() const { std::vector aInds; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); const MeshPointArray& rPoints = _rclMesh.GetPoints(); FacetIndex ind = 0; for (auto it = rFaces.begin(); it != rFaces.end(); ++it, ind++) { if (!it->IsValid()) { aInds.push_back(ind); } else if (!rPoints[it->_aulPoints[0]].IsValid()) { aInds.push_back(ind); } else if (!rPoints[it->_aulPoints[1]].IsValid()) { aInds.push_back(ind); } else if (!rPoints[it->_aulPoints[2]].IsValid()) { aInds.push_back(ind); } } return aInds; } bool MeshFixInvalids::Fixup() { _rclMesh.RemoveInvalids(); return true; } // ---------------------------------------------------------------------- namespace MeshCore { using VertexIterator = MeshPointArray::_TConstIterator; /* * When building up a mesh then usually the class MeshBuilder is used. This * class uses internally a std::set which uses the '<' operator of * MeshPoint to sort the points. Thus to be consistent (and avoid using the * '==' operator of MeshPoint) we use the same operator when comparing the * points in the function object. */ struct Vertex_EqualTo { bool operator()(const VertexIterator& x, const VertexIterator& y) const { if ((*x) < (*y)) { return false; } if ((*y) < (*x)) { return false; } return true; } }; struct Vertex_Less { bool operator()(const VertexIterator& x, const VertexIterator& y) const { return (*x) < (*y); } }; } // namespace MeshCore bool MeshEvalDuplicatePoints::Evaluate() { // get an const iterator to each vertex and sort them in ascending order by // their (x,y,z) coordinates const MeshPointArray& rPoints = _rclMesh.GetPoints(); std::vector vertices; vertices.reserve(rPoints.size()); for (auto it = rPoints.begin(); it != rPoints.end(); ++it) { vertices.push_back(it); } // if there are two adjacent vertices which have the same coordinates std::sort(vertices.begin(), vertices.end(), Vertex_Less()); return (std::adjacent_find(vertices.begin(), vertices.end(), Vertex_EqualTo()) == vertices.end()); } std::vector MeshEvalDuplicatePoints::GetIndices() const { // Note: We must neither use map or set to get duplicated indices because // the sort algorithms deliver different results compared to std::sort of // a vector. const MeshPointArray& rPoints = _rclMesh.GetPoints(); std::vector vertices; vertices.reserve(rPoints.size()); for (auto it = rPoints.begin(); it != rPoints.end(); ++it) { vertices.push_back(it); } // if there are two adjacent vertices which have the same coordinates std::vector aInds; Vertex_EqualTo pred; std::sort(vertices.begin(), vertices.end(), Vertex_Less()); std::vector::iterator vt = vertices.begin(); while (vt < vertices.end()) { // get first item which adjacent element has the same vertex vt = std::adjacent_find(vt, vertices.end(), pred); if (vt < vertices.end()) { ++vt; aInds.push_back(*vt - rPoints.begin()); } } return aInds; } bool MeshFixDuplicatePoints::Fixup() { // Note: We must neither use map or set to get duplicated indices because // the sort algorithms deliver different results compared to std::sort of // a vector. const MeshPointArray& rPoints = _rclMesh.GetPoints(); std::vector vertices; vertices.reserve(rPoints.size()); for (auto it = rPoints.begin(); it != rPoints.end(); ++it) { vertices.push_back(it); } // get the indices of adjacent vertices which have the same coordinates std::sort(vertices.begin(), vertices.end(), Vertex_Less()); Vertex_EqualTo pred; std::vector::iterator next = vertices.begin(); std::map mapPointIndex; std::vector pointIndices; while (next < vertices.end()) { next = std::adjacent_find(next, vertices.end(), pred); if (next < vertices.end()) { auto first = next; PointIndex first_index = *first - rPoints.begin(); ++next; while (next < vertices.end() && pred(*first, *next)) { PointIndex next_index = *next - rPoints.begin(); mapPointIndex[next_index] = first_index; pointIndices.push_back(next_index); ++next; } } } // now set all facets to the correct index MeshFacetArray& rFacets = _rclMesh._aclFacetArray; for (auto& it : rFacets) { for (PointIndex& point : it._aulPoints) { auto pt = mapPointIndex.find(point); if (pt != mapPointIndex.end()) { point = pt->second; } } } // remove invalid indices _rclMesh.DeletePoints(pointIndices); _rclMesh.RebuildNeighbours(); return true; } // ---------------------------------------------------------------------- bool MeshEvalNaNPoints::Evaluate() { const MeshPointArray& rPoints = _rclMesh.GetPoints(); for (const auto& it : rPoints) { if (boost::math::isnan(it.x) || boost::math::isnan(it.y) || boost::math::isnan(it.z)) { return false; } } return true; } std::vector MeshEvalNaNPoints::GetIndices() const { std::vector aInds; const MeshPointArray& rPoints = _rclMesh.GetPoints(); for (auto it = rPoints.begin(); it != rPoints.end(); ++it) { if (boost::math::isnan(it->x) || boost::math::isnan(it->y) || boost::math::isnan(it->z)) { aInds.push_back(it - rPoints.begin()); } } return aInds; } bool MeshFixNaNPoints::Fixup() { std::vector aInds; const MeshPointArray& rPoints = _rclMesh.GetPoints(); for (auto it = rPoints.begin(); it != rPoints.end(); ++it) { if (boost::math::isnan(it->x) || boost::math::isnan(it->y) || boost::math::isnan(it->z)) { aInds.push_back(it - rPoints.begin()); } } // remove invalid indices _rclMesh.DeletePoints(aInds); _rclMesh.RebuildNeighbours(); return true; } // ---------------------------------------------------------------------- namespace MeshCore { using FaceIterator = MeshFacetArray::_TConstIterator; /* * The facet with the lowset index is regarded as 'less'. */ struct MeshFacet_Less { bool operator()(const FaceIterator& x, const FaceIterator& y) const { PointIndex tmp {}; PointIndex x0 = x->_aulPoints[0]; PointIndex x1 = x->_aulPoints[1]; PointIndex x2 = x->_aulPoints[2]; PointIndex y0 = y->_aulPoints[0]; PointIndex y1 = y->_aulPoints[1]; PointIndex y2 = y->_aulPoints[2]; if (x0 > x1) { tmp = x0; x0 = x1; x1 = tmp; } if (x0 > x2) { tmp = x0; x0 = x2; x2 = tmp; } if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; } if (y0 > y1) { tmp = y0; y0 = y1; y1 = tmp; } if (y0 > y2) { tmp = y0; y0 = y2; y2 = tmp; } if (y1 > y2) { tmp = y1; y1 = y2; y2 = tmp; } if (x0 < y0) { return true; } if (x0 > y0) { return false; } if (x1 < y1) { return true; } if (x1 > y1) { return false; } if (x2 < y2) { return true; } return false; } }; } // namespace MeshCore /* * Two facets are equal if all its three point indices refer to the same * location in the point array of the mesh kernel they belong to. */ struct MeshFacet_EqualTo { bool operator()(const FaceIterator& x, const FaceIterator& y) const { for (int i = 0; i < 3; i++) { if (x->_aulPoints[0] == y->_aulPoints[i]) { if (x->_aulPoints[1] == y->_aulPoints[(i + 1) % 3] && x->_aulPoints[2] == y->_aulPoints[(i + 2) % 3]) { return true; } if (x->_aulPoints[1] == y->_aulPoints[(i + 2) % 3] && x->_aulPoints[2] == y->_aulPoints[(i + 1) % 3]) { return true; } } } return false; } }; bool MeshEvalDuplicateFacets::Evaluate() { std::set aFaces; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); for (auto it = rFaces.begin(); it != rFaces.end(); ++it) { std::pair::iterator, bool> pI = aFaces.insert(it); if (!pI.second) { return false; } } return true; } std::vector MeshEvalDuplicateFacets::GetIndices() const { #if 1 const MeshFacetArray& rFacets = _rclMesh.GetFacets(); std::vector faces; faces.reserve(rFacets.size()); for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) { faces.push_back(it); } // if there are two adjacent faces which references the same vertices std::vector aInds; MeshFacet_EqualTo pred; std::sort(faces.begin(), faces.end(), MeshFacet_Less()); std::vector::iterator ft = faces.begin(); while (ft < faces.end()) { // get first item which adjacent element has the same face ft = std::adjacent_find(ft, faces.end(), pred); if (ft < faces.end()) { ++ft; aInds.push_back(*ft - rFacets.begin()); } } return aInds; #else std::vector aInds; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); FacetIndex uIndex = 0; // get all facets std::set aFaceSet; for (MeshFacetArray::_TConstIterator it = rFaces.begin(); it != rFaces.end(); ++it, uIndex++) { std::pair::iterator, bool> pI = aFaceSet.insert(it); if (!pI.second) { aInds.push_back(uIndex); } } return aInds; #endif } bool MeshFixDuplicateFacets::Fixup() { FacetIndex uIndex = 0; std::vector aRemoveFaces; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); // get all facets std::set aFaceSet; for (auto it = rFaces.begin(); it != rFaces.end(); ++it, uIndex++) { std::pair::iterator, bool> pI = aFaceSet.insert(it); if (!pI.second) { aRemoveFaces.push_back(uIndex); } } _rclMesh.DeleteFacets(aRemoveFaces); _rclMesh.RebuildNeighbours(); // needs to be done here return true; } // ---------------------------------------------------------------------- bool MeshEvalInternalFacets::Evaluate() { _indices.clear(); FacetIndex uIndex = 0; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); // get all facets std::set aFaceSet; MeshFacetArray::_TConstIterator first = rFaces.begin(); for (auto it = rFaces.begin(); it != rFaces.end(); ++it, uIndex++) { std::pair::iterator, bool> pI = aFaceSet.insert(it); if (!pI.second) { // collect both elements _indices.push_back(*pI.first - first); _indices.push_back(uIndex); } } return _indices.empty(); } // ---------------------------------------------------------------------- bool MeshEvalDegeneratedFacets::Evaluate() { MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { if (it->IsDegenerated(fEpsilon)) { return false; } } return true; } unsigned long MeshEvalDegeneratedFacets::CountEdgeTooSmall(float fMinEdgeLength) const { MeshFacetIterator clFIter(_rclMesh); unsigned long k = 0; while (!clFIter.EndReached()) { for (int i = 0; i < 3; i++) { if (Base::Distance(clFIter->_aclPoints[i], clFIter->_aclPoints[(i + 1) % 3]) < fMinEdgeLength) { k++; } } ++clFIter; } return k; } std::vector MeshEvalDegeneratedFacets::GetIndices() const { std::vector aInds; MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { if (it->IsDegenerated(fEpsilon)) { aInds.push_back(it.Position()); } } return aInds; } bool MeshFixDegeneratedFacets::Fixup() { MeshTopoAlgorithm cTopAlg(_rclMesh); MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { if (it->IsDegenerated(fEpsilon)) { FacetIndex uId = it.Position(); bool removed = cTopAlg.RemoveDegeneratedFacet(uId); if (removed) { // due to a modification of the array the iterator became invalid it.Set(uId - 1); } } } return true; } bool MeshRemoveNeedles::Fixup() { using FaceEdge = std::pair; // (face, edge) pair using FaceEdgePriority = std::pair; MeshTopoAlgorithm topAlg(_rclMesh); MeshRefPointToFacets vf_it(_rclMesh); const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); const MeshPointArray& rclPAry = _rclMesh.GetPoints(); rclFAry.ResetInvalid(); rclPAry.ResetInvalid(); rclPAry.ResetFlag(MeshPoint::VISIT); std::size_t facetCount = rclFAry.size(); std::priority_queue, std::greater<>> todo; for (std::size_t index = 0; index < facetCount; index++) { const MeshFacet& facet = rclFAry[index]; MeshGeomFacet tria(_rclMesh.GetFacet(facet)); float perimeter = tria.Perimeter(); float fMinLen = perimeter * fMinEdgeLength; for (int i = 0; i < 3; i++) { const Base::Vector3f& p1 = rclPAry[facet._aulPoints[i]]; const Base::Vector3f& p2 = rclPAry[facet._aulPoints[(i + 1) % 3]]; float distance = Base::Distance(p1, p2); if (distance < fMinLen) { unsigned long facetIndex = static_cast(index); todo.push(std::make_pair(distance, std::make_pair(facetIndex, i))); } } } bool removedEdge = false; while (!todo.empty()) { FaceEdge faceedge = todo.top().second; todo.pop(); // check if one of the face pairs was already processed if (!rclFAry[faceedge.first].IsValid()) { continue; } // the facet points may have changed, so check the current distance again const MeshFacet& facet = rclFAry[faceedge.first]; MeshGeomFacet tria(_rclMesh.GetFacet(facet)); float perimeter = tria.Perimeter(); float fMinLen = perimeter * fMinEdgeLength; const Base::Vector3f& p1 = rclPAry[facet._aulPoints[faceedge.second]]; const Base::Vector3f& p2 = rclPAry[facet._aulPoints[(faceedge.second + 1) % 3]]; float distance = Base::Distance(p1, p2); if (distance >= fMinLen) { continue; } // collect the collapse-edge information EdgeCollapse ce; ce._fromPoint = rclFAry[faceedge.first]._aulPoints[faceedge.second]; ce._toPoint = rclFAry[faceedge.first]._aulPoints[(faceedge.second + 1) % 3]; ce._removeFacets.push_back(faceedge.first); FacetIndex neighbour = rclFAry[faceedge.first]._aulNeighbours[faceedge.second]; if (neighbour != FACET_INDEX_MAX) { ce._removeFacets.push_back(neighbour); } std::set vf = vf_it[ce._fromPoint]; vf.erase(faceedge.first); if (neighbour != FACET_INDEX_MAX) { vf.erase(neighbour); } ce._changeFacets.insert(ce._changeFacets.begin(), vf.begin(), vf.end()); // get adjacent points std::set vv; vv = vf_it.NeighbourPoints(ce._fromPoint); ce._adjacentFrom.insert(ce._adjacentFrom.begin(), vv.begin(), vv.end()); vv = vf_it.NeighbourPoints(ce._toPoint); ce._adjacentTo.insert(ce._adjacentTo.begin(), vv.begin(), vv.end()); if (topAlg.IsCollapseEdgeLegal(ce)) { topAlg.CollapseEdge(ce); for (auto it : ce._removeFacets) { vf_it.RemoveFacet(it); } for (auto it : ce._changeFacets) { vf_it.RemoveNeighbour(ce._fromPoint, it); vf_it.AddNeighbour(ce._toPoint, it); } removedEdge = true; } } if (removedEdge) { topAlg.Cleanup(); _rclMesh.RebuildNeighbours(); } return true; } // ---------------------------------------------------------------------- bool MeshFixCaps::Fixup() { using FaceVertex = std::pair; // (face, vertex) pair using FaceVertexPriority = std::pair; MeshTopoAlgorithm topAlg(_rclMesh); const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); const MeshPointArray& rclPAry = _rclMesh.GetPoints(); std::size_t facetCount = rclFAry.size(); float fCosMaxAngle = static_cast(cos(fMaxAngle)); std::priority_queue, std::greater<>> todo; for (std::size_t index = 0; index < facetCount; index++) { for (int i = 0; i < 3; i++) { const MeshFacet& facet = rclFAry[index]; const Base::Vector3f& p1 = rclPAry[facet._aulPoints[i]]; const Base::Vector3f& p2 = rclPAry[facet._aulPoints[(i + 1) % 3]]; const Base::Vector3f& p3 = rclPAry[facet._aulPoints[(i + 2) % 3]]; Base::Vector3f dir1(p2 - p1); dir1.Normalize(); Base::Vector3f dir2(p3 - p1); dir2.Normalize(); float fCosAngle = dir1.Dot(dir2); if (fCosAngle < fCosMaxAngle) { unsigned long facetIndex = static_cast(index); todo.push(std::make_pair(fCosAngle, std::make_pair(facetIndex, i))); } } } while (!todo.empty()) { FaceVertex facevertex = todo.top().second; todo.pop(); // the facet points may have changed, so check the current distance again const MeshFacet& facet = rclFAry[facevertex.first]; const Base::Vector3f& p1 = rclPAry[facet._aulPoints[facevertex.second]]; const Base::Vector3f& p2 = rclPAry[facet._aulPoints[(facevertex.second + 1) % 3]]; const Base::Vector3f& p3 = rclPAry[facet._aulPoints[(facevertex.second + 2) % 3]]; Base::Vector3f dir1(p2 - p1); dir1.Normalize(); Base::Vector3f dir2(p3 - p1); dir2.Normalize(); // check that the criterion is still OK in case // an earlier edge-swap has an impact float fCosAngle = dir1.Dot(dir2); if (fCosAngle >= fCosMaxAngle) { continue; } // the triangle shouldn't be a needle, therefore the projection of the point with // the maximum angle must have a clear distance to the other corner points // as factor we choose a default value of 25% of the corresponding edge length Base::Vector3f p4 = p1.Perpendicular(p2, p3 - p2); float distP2P3 = Base::Distance(p2, p3); float distP2P4 = Base::Distance(p2, p4); float distP3P4 = Base::Distance(p3, p4); if (distP2P4 / distP2P3 < fSplitFactor || distP3P4 / distP2P3 < fSplitFactor) { continue; } FacetIndex facetpos = facevertex.first; FacetIndex neighbour = rclFAry[facetpos]._aulNeighbours[(facevertex.second + 1) % 3]; if (neighbour != FACET_INDEX_MAX) { topAlg.SwapEdge(facetpos, neighbour); } } return true; } // ---------------------------------------------------------------------- bool MeshEvalDeformedFacets::Evaluate() { float fCosMinAngle = cos(fMinAngle); float fCosMaxAngle = cos(fMaxAngle); MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { if (it->IsDeformed(fCosMinAngle, fCosMaxAngle)) { return false; } } return true; } std::vector MeshEvalDeformedFacets::GetIndices() const { float fCosMinAngle = cos(fMinAngle); float fCosMaxAngle = cos(fMaxAngle); std::vector aInds; MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { if (it->IsDeformed(fCosMinAngle, fCosMaxAngle)) { aInds.push_back(it.Position()); } } return aInds; } bool MeshFixDeformedFacets::Fixup() { float fCosMinAngle = cos(fMinAngle); float fCosMaxAngle = cos(fMaxAngle); Base::Vector3f u, v; MeshTopoAlgorithm cTopAlg(_rclMesh); MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { // possibly deformed but not degenerated if (!it->IsDegenerated(fEpsilon)) { // store the angles to avoid to compute twice float fCosAngles[3] = {0, 0, 0}; bool done = false; for (int i = 0; i < 3; i++) { u = it->_aclPoints[(i + 1) % 3] - it->_aclPoints[i]; v = it->_aclPoints[(i + 2) % 3] - it->_aclPoints[i]; u.Normalize(); v.Normalize(); float fCosAngle = u * v; fCosAngles[i] = fCosAngle; } // first check for angle > 120 deg: in this case we swap with the opposite edge for (int i = 0; i < 3; i++) { float fCosAngle = fCosAngles[i]; if (fCosAngle < fCosMaxAngle) { const MeshFacet& face = it.GetReference(); FacetIndex uNeighbour = face._aulNeighbours[(i + 1) % 3]; if (uNeighbour != FACET_INDEX_MAX && cTopAlg.ShouldSwapEdge(it.Position(), uNeighbour, fMaxSwapAngle)) { cTopAlg.SwapEdge(it.Position(), uNeighbour); done = true; } break; } } // we have swapped already if (done) { continue; } // now check for angle < 30 deg: in this case we swap with one of the edges the corner // is part of for (int j = 0; j < 3; j++) { float fCosAngle = fCosAngles[j]; if (fCosAngle > fCosMinAngle) { const MeshFacet& face = it.GetReference(); FacetIndex uNeighbour = face._aulNeighbours[j]; if (uNeighbour != FACET_INDEX_MAX && cTopAlg.ShouldSwapEdge(it.Position(), uNeighbour, fMaxSwapAngle)) { cTopAlg.SwapEdge(it.Position(), uNeighbour); break; } uNeighbour = face._aulNeighbours[(j + 2) % 3]; if (uNeighbour != FACET_INDEX_MAX && cTopAlg.ShouldSwapEdge(it.Position(), uNeighbour, fMaxSwapAngle)) { cTopAlg.SwapEdge(it.Position(), uNeighbour); break; } } } } } return true; } // ---------------------------------------------------------------------- bool MeshFixMergeFacets::Fixup() { MeshCore::MeshRefPointToPoints vv_it(_rclMesh); MeshCore::MeshRefPointToFacets vf_it(_rclMesh); unsigned long countPoints = _rclMesh.CountPoints(); std::vector newFacets; newFacets.reserve(countPoints / 20); // 5% should be sufficient MeshTopoAlgorithm topAlg(_rclMesh); for (unsigned long i = 0; i < countPoints; i++) { if (vv_it[i].size() == 3 && vf_it[i].size() == 3) { VertexCollapse vc; vc._point = i; const std::set& adjPts = vv_it[i]; vc._circumPoints.insert(vc._circumPoints.begin(), adjPts.begin(), adjPts.end()); const std::set& adjFts = vf_it[i]; vc._circumFacets.insert(vc._circumFacets.begin(), adjFts.begin(), adjFts.end()); topAlg.CollapseVertex(vc); } } topAlg.Cleanup(); return true; } // ---------------------------------------------------------------------- bool MeshEvalDentsOnSurface::Evaluate() { this->indices.clear(); MeshRefPointToFacets clPt2Facets(_rclMesh); const MeshPointArray& rPntAry = _rclMesh.GetPoints(); MeshFacetArray::_TConstIterator f_beg = _rclMesh.GetFacets().begin(); MeshGeomFacet rTriangle; Base::Vector3f tmp; unsigned long ctPoints = _rclMesh.CountPoints(); for (unsigned long index = 0; index < ctPoints; index++) { std::vector point; point.push_back(index); // get the local neighbourhood of the point std::set nb = clPt2Facets.NeighbourPoints(point, 1); const std::set& faces = clPt2Facets[index]; for (PointIndex pt : nb) { const MeshPoint& mp = rPntAry[pt]; for (FacetIndex ft : faces) { // the point must not be part of the facet we test if (f_beg[ft]._aulPoints[0] == pt) { continue; } if (f_beg[ft]._aulPoints[1] == pt) { continue; } if (f_beg[ft]._aulPoints[2] == pt) { continue; } // is the point projectable onto the facet? rTriangle = _rclMesh.GetFacet(f_beg[ft]); if (rTriangle.IntersectWithLine(mp, rTriangle.GetNormal(), tmp)) { const std::set& f = clPt2Facets[pt]; this->indices.insert(this->indices.end(), f.begin(), f.end()); break; } } } } // remove duplicates std::sort(this->indices.begin(), this->indices.end()); this->indices.erase(std::unique(this->indices.begin(), this->indices.end()), this->indices.end()); return this->indices.empty(); } std::vector MeshEvalDentsOnSurface::GetIndices() const { return this->indices; } /* Forbidden is: + two facets share a common point but not a common edge Repair: + store the point indices which can be projected on a face + store the face indices on which a point can be projected + remove faces with an edge length smaller than a certain threshold (e.g. 0.01) from the stored triangles or that reference one of the stored points + for this edge merge the two points + if a point of a face can be projected onto another face and they have a common point then split the second face if the distance is under a certain threshold */ bool MeshFixDentsOnSurface::Fixup() { MeshEvalDentsOnSurface eval(_rclMesh); if (!eval.Evaluate()) { std::vector inds = eval.GetIndices(); _rclMesh.DeleteFacets(inds); } return true; } // ---------------------------------------------------------------------- bool MeshEvalFoldsOnSurface::Evaluate() { this->indices.clear(); const MeshFacetArray& rFAry = _rclMesh.GetFacets(); unsigned long ct = 0; for (auto it = rFAry.begin(); it != rFAry.end(); ++it, ct++) { for (int i = 0; i < 3; i++) { FacetIndex n1 = it->_aulNeighbours[i]; FacetIndex n2 = it->_aulNeighbours[(i + 1) % 3]; Base::Vector3f v1 = _rclMesh.GetFacet(*it).GetNormal(); if (n1 != FACET_INDEX_MAX && n2 != FACET_INDEX_MAX) { Base::Vector3f v2 = _rclMesh.GetFacet(n1).GetNormal(); Base::Vector3f v3 = _rclMesh.GetFacet(n2).GetNormal(); if (v2 * v3 > 0.0F) { if (v1 * v2 < -0.1F && v1 * v3 < -0.1F) { indices.push_back(n1); indices.push_back(n2); indices.push_back(ct); } } } } } // remove duplicates std::sort(this->indices.begin(), this->indices.end()); this->indices.erase(std::unique(this->indices.begin(), this->indices.end()), this->indices.end()); return this->indices.empty(); } std::vector MeshEvalFoldsOnSurface::GetIndices() const { return this->indices; } // ---------------------------------------------------------------------- bool MeshEvalFoldsOnBoundary::Evaluate() { // remove all boundary facets with two open edges and where // the angle to the neighbour is more than 60 degree this->indices.clear(); const MeshFacetArray& rFacAry = _rclMesh.GetFacets(); for (auto it = rFacAry.begin(); it != rFacAry.end(); ++it) { if (it->CountOpenEdges() == 2) { for (FacetIndex nbIndex : it->_aulNeighbours) { if (nbIndex != FACET_INDEX_MAX) { MeshGeomFacet f1 = _rclMesh.GetFacet(*it); MeshGeomFacet f2 = _rclMesh.GetFacet(nbIndex); float cos_angle = f1.GetNormal() * f2.GetNormal(); if (cos_angle <= 0.5F) { // ~ 60 degree indices.push_back(it - rFacAry.begin()); } } } } } return this->indices.empty(); } std::vector MeshEvalFoldsOnBoundary::GetIndices() const { return this->indices; } bool MeshFixFoldsOnBoundary::Fixup() { MeshEvalFoldsOnBoundary eval(_rclMesh); if (!eval.Evaluate()) { std::vector inds = eval.GetIndices(); _rclMesh.DeleteFacets(inds); } return true; } // ---------------------------------------------------------------------- bool MeshEvalFoldOversOnSurface::Evaluate() { this->indices.clear(); const MeshCore::MeshFacetArray& facets = _rclMesh.GetFacets(); MeshCore::MeshFacetArray::_TConstIterator f_it, f_beg = facets.begin(), f_end = facets.end(); Base::Vector3f n1, n2; for (f_it = facets.begin(); f_it != f_end; ++f_it) { for (int i = 0; i < 3; i++) { FacetIndex index1 = f_it->_aulNeighbours[i]; FacetIndex index2 = f_it->_aulNeighbours[(i + 1) % 3]; if (index1 != FACET_INDEX_MAX && index2 != FACET_INDEX_MAX) { // if the topology is correct but the normals flip from // two neighbours we have a fold if (f_it->HasSameOrientation(f_beg[index1]) && f_it->HasSameOrientation(f_beg[index2])) { n1 = _rclMesh.GetFacet(index1).GetNormal(); n2 = _rclMesh.GetFacet(index2).GetNormal(); if (n1 * n2 < -0.5F) { // angle > 120 deg this->indices.push_back(f_it - f_beg); break; } } } } } return this->indices.empty(); } // ---------------------------------------------------------------- bool MeshEvalBorderFacet::Evaluate() { const MeshCore::MeshFacetArray& facets = _rclMesh.GetFacets(); MeshCore::MeshFacetArray::_TConstIterator f_it, f_beg = facets.begin(), f_end = facets.end(); MeshCore::MeshRefPointToPoints vv_it(_rclMesh); MeshCore::MeshRefPointToFacets vf_it(_rclMesh); for (f_it = facets.begin(); f_it != f_end; ++f_it) { bool ok = true; for (PointIndex index : f_it->_aulPoints) { if (vv_it[index].size() == vf_it[index].size()) { ok = false; break; } } if (ok) { _facets.push_back(f_it - f_beg); } } return _facets.empty(); } // ---------------------------------------------------------------------- bool MeshEvalRangeFacet::Evaluate() { const MeshFacetArray& rFaces = _rclMesh.GetFacets(); FacetIndex ulCtFacets = rFaces.size(); for (const auto& it : rFaces) { for (FacetIndex nbFacet : it._aulNeighbours) { if ((nbFacet >= ulCtFacets) && (nbFacet < FACET_INDEX_MAX)) { return false; } } } return true; } std::vector MeshEvalRangeFacet::GetIndices() const { std::vector aInds; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); FacetIndex ulCtFacets = rFaces.size(); FacetIndex ind = 0; for (auto it = rFaces.begin(); it != rFaces.end(); ++it, ind++) { for (FacetIndex nbIndex : it->_aulNeighbours) { if ((nbIndex >= ulCtFacets) && (nbIndex < FACET_INDEX_MAX)) { aInds.push_back(ind); break; } } } return aInds; } bool MeshFixRangeFacet::Fixup() { _rclMesh.RebuildNeighbours(); return true; } // ---------------------------------------------------------------------- bool MeshEvalRangePoint::Evaluate() { const MeshFacetArray& rFaces = _rclMesh.GetFacets(); PointIndex ulCtPoints = _rclMesh.CountPoints(); for (const auto& it : rFaces) { if (std::find_if( it._aulPoints, it._aulPoints + 3, [ulCtPoints](PointIndex i) { return i >= ulCtPoints; } ) < it._aulPoints + 3) { return false; } } return true; } std::vector MeshEvalRangePoint::GetIndices() const { std::vector aInds; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); PointIndex ulCtPoints = _rclMesh.CountPoints(); PointIndex ind = 0; for (auto it = rFaces.begin(); it != rFaces.end(); ++it, ind++) { if (std::find_if( it->_aulPoints, it->_aulPoints + 3, [ulCtPoints](PointIndex i) { return i >= ulCtPoints; } ) < it->_aulPoints + 3) { aInds.push_back(ind); } } return aInds; } bool MeshFixRangePoint::Fixup() { MeshEvalRangePoint eval(_rclMesh); if (_rclMesh.CountPoints() == 0) { // if no points are there but facets then the whole mesh can be cleared _rclMesh.Clear(); } else { // facets with point indices out of range cannot be directly deleted because // 'DeleteFacets' will segfault. But setting all point indices to 0 works. std::vector invalid = eval.GetIndices(); if (!invalid.empty()) { for (PointIndex it : invalid) { _rclMesh.SetFacetPoints(it, 0, 0, 0); } _rclMesh.DeleteFacets(invalid); } } return true; } // ---------------------------------------------------------------------- bool MeshEvalCorruptedFacets::Evaluate() { const MeshFacetArray& rFaces = _rclMesh.GetFacets(); for (const auto& it : rFaces) { // duplicated point indices if (it.IsDegenerated()) { return false; } } return true; } std::vector MeshEvalCorruptedFacets::GetIndices() const { std::vector aInds; const MeshFacetArray& rFaces = _rclMesh.GetFacets(); FacetIndex ind = 0; for (auto it = rFaces.begin(); it != rFaces.end(); ++it, ind++) { if (it->IsDegenerated()) { aInds.push_back(ind); } } return aInds; } bool MeshFixCorruptedFacets::Fixup() { MeshTopoAlgorithm cTopAlg(_rclMesh); MeshFacetIterator it(_rclMesh); for (it.Init(); it.More(); it.Next()) { if (it.GetReference().IsDegenerated()) { unsigned long uId = it.Position(); bool removed = cTopAlg.RemoveCorruptedFacet(uId); if (removed) { // due to a modification of the array the iterator became invalid it.Set(uId - 1); } } } return true; } // ---------------------------------------------------------------------- bool MeshEvalPointOnEdge::Evaluate() { MeshFacetGrid facetGrid(_rclMesh); const MeshPointArray& points = _rclMesh.GetPoints(); const MeshFacetArray& facets = _rclMesh.GetFacets(); auto IsPointOnEdge = [&points](PointIndex idx, const MeshFacet& facet) { // point must not be a corner of the facet if (!facet.HasPoint(idx)) { for (int i = 0; i < 3; i++) { MeshGeomEdge edge; edge._aclPoints[0] = points[facet._aulPoints[i]]; edge._aclPoints[1] = points[facet._aulPoints[(i + 1) % 3]]; if (edge.GetBoundBox().IsInBox(points[idx])) { if (edge.IsPointOf(points[idx], 0.001F)) { return true; } } } } return false; }; PointIndex maxPoints = _rclMesh.CountPoints(); for (PointIndex i = 0; i < maxPoints; i++) { std::vector elements; facetGrid.GetElements(points[i], elements); for (const auto& it : elements) { const MeshFacet& face = facets[it]; if (IsPointOnEdge(i, face)) { pointsIndices.push_back(i); if (face.HasOpenEdge()) { facetsIndices.push_back(it); } } } } return pointsIndices.empty(); } std::vector MeshEvalPointOnEdge::GetPointIndices() const { return pointsIndices; } std::vector MeshEvalPointOnEdge::GetFacetIndices() const { return facetsIndices; } bool MeshFixPointOnEdge::Fixup() { MeshEvalPointOnEdge eval(_rclMesh); eval.Evaluate(); std::vector pointsIndices = eval.GetPointIndices(); std::vector facetsIndices = eval.GetFacetIndices(); if (!pointsIndices.empty()) { if (fillBoundary) { MarkBoundaries(facetsIndices); } _rclMesh.DeletePoints(pointsIndices); if (fillBoundary) { std::list> borderList; FindBoundaries(borderList); if (!borderList.empty()) { FillBoundaries(borderList); } } } return true; } void MeshFixPointOnEdge::MarkBoundaries(const std::vector& facetsIndices) { MeshAlgorithm meshalg(_rclMesh); meshalg.ResetFacetFlag(MeshFacet::TMP0); meshalg.SetFacetsFlag(facetsIndices, MeshFacet::TMP0); } void MeshFixPointOnEdge::FindBoundaries(std::list>& borderList) { std::vector tmp; MeshAlgorithm meshalg(_rclMesh); meshalg.GetFacetsFlag(tmp, MeshFacet::TMP0); if (!tmp.empty()) { meshalg.GetFacetsBorders(tmp, borderList); } } void MeshFixPointOnEdge::FillBoundaries(const std::list>& borderList) { FlatTriangulator tria; tria.SetVerifier(new MeshCore::TriangulationVerifierV2); MeshTopoAlgorithm topalg(_rclMesh); std::list> failed; topalg.FillupHoles(1, tria, borderList, failed); }