Download src/Mod/Mesh/App/Core/Degeneration.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 43.3 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/Degeneration.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Mesh/App/Core/Degeneration.cpp
-
curl -L -o Degeneration.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/Degeneration.cpp
43.3 kB
| // 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 * | |
| * * | |
| ***************************************************************************/ | |
| 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<FacetIndex> MeshEvalInvalids::GetIndices() const | |
| { | |
| std::vector<FacetIndex> 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<MeshPoint> 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<VertexIterator> 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<PointIndex> 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<VertexIterator> 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<PointIndex> aInds; | |
| Vertex_EqualTo pred; | |
| std::sort(vertices.begin(), vertices.end(), Vertex_Less()); | |
| std::vector<VertexIterator>::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<VertexIterator> 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<VertexIterator>::iterator next = vertices.begin(); | |
| std::map<PointIndex, PointIndex> mapPointIndex; | |
| std::vector<PointIndex> 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<PointIndex> MeshEvalNaNPoints::GetIndices() const | |
| { | |
| std::vector<PointIndex> 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<PointIndex> 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<FaceIterator, MeshFacet_Less> aFaces; | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| for (auto it = rFaces.begin(); it != rFaces.end(); ++it) { | |
| std::pair<std::set<FaceIterator, MeshFacet_Less>::iterator, bool> pI = aFaces.insert(it); | |
| if (!pI.second) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| std::vector<FacetIndex> MeshEvalDuplicateFacets::GetIndices() const | |
| { | |
| const MeshFacetArray& rFacets = _rclMesh.GetFacets(); | |
| std::vector<FaceIterator> 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<FacetIndex> aInds; | |
| MeshFacet_EqualTo pred; | |
| std::sort(faces.begin(), faces.end(), MeshFacet_Less()); | |
| std::vector<FaceIterator>::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; | |
| std::vector<FacetIndex> aInds; | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| FacetIndex uIndex = 0; | |
| // get all facets | |
| std::set<FaceIterator, MeshFacet_Less> aFaceSet; | |
| for (MeshFacetArray::_TConstIterator it = rFaces.begin(); it != rFaces.end(); ++it, uIndex++) { | |
| std::pair<std::set<FaceIterator, MeshFacet_Less>::iterator, bool> pI = aFaceSet.insert(it); | |
| if (!pI.second) { | |
| aInds.push_back(uIndex); | |
| } | |
| } | |
| return aInds; | |
| } | |
| bool MeshFixDuplicateFacets::Fixup() | |
| { | |
| FacetIndex uIndex = 0; | |
| std::vector<FacetIndex> aRemoveFaces; | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| // get all facets | |
| std::set<FaceIterator, MeshFacet_Less> aFaceSet; | |
| for (auto it = rFaces.begin(); it != rFaces.end(); ++it, uIndex++) { | |
| std::pair<std::set<FaceIterator, MeshFacet_Less>::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<FaceIterator, MeshFacet_Less> aFaceSet; | |
| MeshFacetArray::_TConstIterator first = rFaces.begin(); | |
| for (auto it = rFaces.begin(); it != rFaces.end(); ++it, uIndex++) { | |
| std::pair<std::set<FaceIterator, MeshFacet_Less>::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<FacetIndex> MeshEvalDegeneratedFacets::GetIndices() const | |
| { | |
| std::vector<FacetIndex> 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<unsigned long, int>; // (face, edge) pair | |
| using FaceEdgePriority = std::pair<float, FaceEdge>; | |
| 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<FaceEdgePriority, std::vector<FaceEdgePriority>, 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<unsigned long>(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<FacetIndex> 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<PointIndex> 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<unsigned long, int>; // (face, vertex) pair | |
| using FaceVertexPriority = std::pair<float, FaceVertex>; | |
| MeshTopoAlgorithm topAlg(_rclMesh); | |
| const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); | |
| const MeshPointArray& rclPAry = _rclMesh.GetPoints(); | |
| std::size_t facetCount = rclFAry.size(); | |
| float fCosMaxAngle = static_cast<float>(cos(fMaxAngle)); | |
| std::priority_queue<FaceVertexPriority, std::vector<FaceVertexPriority>, 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<unsigned long>(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<FacetIndex> MeshEvalDeformedFacets::GetIndices() const | |
| { | |
| float fCosMinAngle = cos(fMinAngle); | |
| float fCosMaxAngle = cos(fMaxAngle); | |
| std::vector<FacetIndex> 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<MeshFacet> 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<PointIndex>& adjPts = vv_it[i]; | |
| vc._circumPoints.insert(vc._circumPoints.begin(), adjPts.begin(), adjPts.end()); | |
| const std::set<FacetIndex>& 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<PointIndex> point; | |
| point.push_back(index); | |
| // get the local neighbourhood of the point | |
| std::set<PointIndex> nb = clPt2Facets.NeighbourPoints(point, 1); | |
| const std::set<FacetIndex>& 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<FacetIndex>& 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<FacetIndex> 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<FacetIndex> 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<FacetIndex> 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<FacetIndex> MeshEvalFoldsOnBoundary::GetIndices() const | |
| { | |
| return this->indices; | |
| } | |
| bool MeshFixFoldsOnBoundary::Fixup() | |
| { | |
| MeshEvalFoldsOnBoundary eval(_rclMesh); | |
| if (!eval.Evaluate()) { | |
| std::vector<FacetIndex> 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<FacetIndex> MeshEvalRangeFacet::GetIndices() const | |
| { | |
| std::vector<FacetIndex> 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<PointIndex> MeshEvalRangePoint::GetIndices() const | |
| { | |
| std::vector<PointIndex> 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<PointIndex> 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<FacetIndex> MeshEvalCorruptedFacets::GetIndices() const | |
| { | |
| std::vector<FacetIndex> 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<FacetIndex> 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<PointIndex> MeshEvalPointOnEdge::GetPointIndices() const | |
| { | |
| return pointsIndices; | |
| } | |
| std::vector<FacetIndex> MeshEvalPointOnEdge::GetFacetIndices() const | |
| { | |
| return facetsIndices; | |
| } | |
| bool MeshFixPointOnEdge::Fixup() | |
| { | |
| MeshEvalPointOnEdge eval(_rclMesh); | |
| eval.Evaluate(); | |
| std::vector<PointIndex> pointsIndices = eval.GetPointIndices(); | |
| std::vector<FacetIndex> facetsIndices = eval.GetFacetIndices(); | |
| if (!pointsIndices.empty()) { | |
| if (fillBoundary) { | |
| MarkBoundaries(facetsIndices); | |
| } | |
| _rclMesh.DeletePoints(pointsIndices); | |
| if (fillBoundary) { | |
| std::list<std::vector<PointIndex>> borderList; | |
| FindBoundaries(borderList); | |
| if (!borderList.empty()) { | |
| FillBoundaries(borderList); | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| void MeshFixPointOnEdge::MarkBoundaries(const std::vector<FacetIndex>& facetsIndices) | |
| { | |
| MeshAlgorithm meshalg(_rclMesh); | |
| meshalg.ResetFacetFlag(MeshFacet::TMP0); | |
| meshalg.SetFacetsFlag(facetsIndices, MeshFacet::TMP0); | |
| } | |
| void MeshFixPointOnEdge::FindBoundaries(std::list<std::vector<PointIndex>>& borderList) | |
| { | |
| std::vector<FacetIndex> tmp; | |
| MeshAlgorithm meshalg(_rclMesh); | |
| meshalg.GetFacetsFlag(tmp, MeshFacet::TMP0); | |
| if (!tmp.empty()) { | |
| meshalg.GetFacetsBorders(tmp, borderList); | |
| } | |
| } | |
| void MeshFixPointOnEdge::FillBoundaries(const std::list<std::vector<PointIndex>>& borderList) | |
| { | |
| FlatTriangulator tria; | |
| tria.SetVerifier(new MeshCore::TriangulationVerifierV2); | |
| MeshTopoAlgorithm topalg(_rclMesh); | |
| std::list<std::vector<PointIndex>> failed; | |
| topalg.FillupHoles(1, tria, borderList, failed); | |
| } | |