Download src/Mod/Mesh/App/Core/TopoAlgorithm.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 70.1 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp
-
curl -L -o TopoAlgorithm.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp
70.1 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; | |
| MeshTopoAlgorithm::MeshTopoAlgorithm(MeshKernel& rclM) | |
| : _rclMesh(rclM) | |
| {} | |
| MeshTopoAlgorithm::~MeshTopoAlgorithm() | |
| { | |
| if (_needsCleanup) { | |
| Cleanup(); | |
| } | |
| EndCache(); | |
| } | |
| bool MeshTopoAlgorithm::InsertVertex(FacetIndex ulFacetPos, const Base::Vector3f& rclPoint) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet clNewFacet1, clNewFacet2; | |
| // insert new point | |
| PointIndex ulPtCnt = _rclMesh._aclPointArray.size(); | |
| PointIndex ulPtInd = this->GetOrAddIndex(rclPoint); | |
| FacetIndex ulSize = _rclMesh._aclFacetArray.size(); | |
| if (ulPtInd < ulPtCnt) { | |
| return false; // the given point is already part of the mesh => creating new facets would | |
| // be an illegal operation | |
| } | |
| // adjust the facets | |
| // | |
| // first new facet | |
| clNewFacet1._aulPoints[0] = rclF._aulPoints[1]; | |
| clNewFacet1._aulPoints[1] = rclF._aulPoints[2]; | |
| clNewFacet1._aulPoints[2] = ulPtInd; | |
| clNewFacet1._aulNeighbours[0] = rclF._aulNeighbours[1]; | |
| clNewFacet1._aulNeighbours[1] = ulSize + 1; | |
| clNewFacet1._aulNeighbours[2] = ulFacetPos; | |
| // second new facet | |
| clNewFacet2._aulPoints[0] = rclF._aulPoints[2]; | |
| clNewFacet2._aulPoints[1] = rclF._aulPoints[0]; | |
| clNewFacet2._aulPoints[2] = ulPtInd; | |
| clNewFacet2._aulNeighbours[0] = rclF._aulNeighbours[2]; | |
| clNewFacet2._aulNeighbours[1] = ulFacetPos; | |
| clNewFacet2._aulNeighbours[2] = ulSize; | |
| // adjust the neighbour facet | |
| if (rclF._aulNeighbours[1] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[1]].ReplaceNeighbour(ulFacetPos, ulSize); | |
| } | |
| if (rclF._aulNeighbours[2] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[2]].ReplaceNeighbour(ulFacetPos, ulSize + 1); | |
| } | |
| // original facet | |
| rclF._aulPoints[2] = ulPtInd; | |
| rclF._aulNeighbours[1] = ulSize; | |
| rclF._aulNeighbours[2] = ulSize + 1; | |
| // insert new facets | |
| _rclMesh._aclFacetArray.push_back(clNewFacet1); | |
| _rclMesh._aclFacetArray.push_back(clNewFacet2); | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::SnapVertex(FacetIndex ulFacetPos, const Base::Vector3f& rP) | |
| { | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; | |
| if (!rFace.HasOpenEdge()) { | |
| return false; | |
| } | |
| Base::Vector3f cNo1 = _rclMesh.GetNormal(rFace); | |
| for (unsigned short i = 0; i < 3; i++) { | |
| if (rFace._aulNeighbours[i] == FACET_INDEX_MAX) { | |
| const Base::Vector3f& rPt1 = _rclMesh._aclPointArray[rFace._aulPoints[i]]; | |
| const Base::Vector3f& rPt2 = _rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]]; | |
| Base::Vector3f cNo2 = (rPt2 - rPt1) % cNo1; | |
| Base::Vector3f cNo3 = (rP - rPt1) % (rPt2 - rPt1); | |
| float fD2 = Base::DistanceP2(rPt1, rPt2); | |
| float fTV = (rP - rPt1) * (rPt2 - rPt1); | |
| // Point is on the edge | |
| if (cNo3.Length() < std::numeric_limits<float>::epsilon()) { | |
| return SplitOpenEdge(ulFacetPos, i, rP); | |
| } | |
| if ((rP - rPt1) * cNo2 > 0.0F && fD2 >= fTV && fTV >= 0.0F) { | |
| MeshFacet cTria; | |
| cTria._aulPoints[0] = this->GetOrAddIndex(rP); | |
| cTria._aulPoints[1] = rFace._aulPoints[(i + 1) % 3]; | |
| cTria._aulPoints[2] = rFace._aulPoints[i]; | |
| cTria._aulNeighbours[1] = ulFacetPos; | |
| rFace._aulNeighbours[i] = _rclMesh.CountFacets(); | |
| _rclMesh._aclFacetArray.push_back(cTria); | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| void MeshTopoAlgorithm::OptimizeTopology(float fMaxAngle) | |
| { | |
| // For each internal edge get the adjacent facets. When doing an edge swap we must update | |
| // this structure. | |
| std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>> aEdge2Face; | |
| for (auto pI = _rclMesh._aclFacetArray.begin(); pI != _rclMesh._aclFacetArray.end(); ++pI) { | |
| for (int i = 0; i < 3; i++) { | |
| // ignore open edges | |
| if (pI->_aulNeighbours[i] != FACET_INDEX_MAX) { | |
| PointIndex ulPt0 = std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| PointIndex ulPt1 = std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| aEdge2Face[std::pair<PointIndex, PointIndex>(ulPt0, ulPt1)].push_back( | |
| pI - _rclMesh._aclFacetArray.begin() | |
| ); | |
| } | |
| } | |
| } | |
| // fill up this list with all internal edges and perform swap edges until this list is empty | |
| std::list<std::pair<PointIndex, PointIndex>> aEdgeList; | |
| std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>>::iterator pE; | |
| for (pE = aEdge2Face.begin(); pE != aEdge2Face.end(); ++pE) { | |
| if (pE->second.size() == 2) { // make sure that we really have an internal edge | |
| aEdgeList.push_back(pE->first); | |
| } | |
| } | |
| // to be sure to avoid an endless loop | |
| unsigned long uMaxIter = 5 * aEdge2Face.size(); | |
| // Perform a swap edge where needed | |
| while (!aEdgeList.empty() && uMaxIter > 0) { | |
| // get the first edge and remove it from the list | |
| std::pair<PointIndex, PointIndex> aEdge = aEdgeList.front(); | |
| aEdgeList.pop_front(); | |
| uMaxIter--; | |
| // get the adjacent facets to this edge | |
| pE = aEdge2Face.find(aEdge); | |
| // this edge has been removed some iterations before | |
| if (pE == aEdge2Face.end()) { | |
| continue; | |
| } | |
| // Is swap edge allowed and sensible? | |
| if (!ShouldSwapEdge(pE->second[0], pE->second[1], fMaxAngle)) { | |
| continue; | |
| } | |
| // ok, here we should perform a swap edge to minimize the maximum angle | |
| if (/*fMax12 > fMax34*/ true) { | |
| // swap the edge | |
| SwapEdge(pE->second[0], pE->second[1]); | |
| MeshFacet& rF1 = _rclMesh._aclFacetArray[pE->second[0]]; | |
| MeshFacet& rF2 = _rclMesh._aclFacetArray[pE->second[1]]; | |
| unsigned short side1 = rF1.Side(aEdge.first, aEdge.second); | |
| unsigned short side2 = rF2.Side(aEdge.first, aEdge.second); | |
| // adjust the edge list | |
| for (int i = 0; i < 3; i++) { | |
| std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>>::iterator it; | |
| // first facet | |
| PointIndex ulPt0 = std::min<PointIndex>(rF1._aulPoints[i], rF1._aulPoints[(i + 1) % 3]); | |
| PointIndex ulPt1 = std::max<PointIndex>(rF1._aulPoints[i], rF1._aulPoints[(i + 1) % 3]); | |
| it = aEdge2Face.find(std::make_pair(ulPt0, ulPt1)); | |
| if (it != aEdge2Face.end()) { | |
| if (it->second[0] == pE->second[1]) { | |
| it->second[0] = pE->second[0]; | |
| } | |
| else if (it->second[1] == pE->second[1]) { | |
| it->second[1] = pE->second[0]; | |
| } | |
| aEdgeList.push_back(it->first); | |
| } | |
| // second facet | |
| ulPt0 = std::min<PointIndex>(rF2._aulPoints[i], rF2._aulPoints[(i + 1) % 3]); | |
| ulPt1 = std::max<PointIndex>(rF2._aulPoints[i], rF2._aulPoints[(i + 1) % 3]); | |
| it = aEdge2Face.find(std::make_pair(ulPt0, ulPt1)); | |
| if (it != aEdge2Face.end()) { | |
| if (it->second[0] == pE->second[0]) { | |
| it->second[0] = pE->second[1]; | |
| } | |
| else if (it->second[1] == pE->second[0]) { | |
| it->second[1] = pE->second[1]; | |
| } | |
| aEdgeList.push_back(it->first); | |
| } | |
| } | |
| // Now we must remove the edge and replace it through the new edge | |
| PointIndex ulPt0 = std::min<PointIndex>( | |
| rF1._aulPoints[(side1 + 1) % 3], | |
| rF2._aulPoints[(side2 + 1) % 3] | |
| ); | |
| PointIndex ulPt1 = std::max<PointIndex>( | |
| rF1._aulPoints[(side1 + 1) % 3], | |
| rF2._aulPoints[(side2 + 1) % 3] | |
| ); | |
| std::pair<PointIndex, PointIndex> aNewEdge = std::make_pair(ulPt0, ulPt1); | |
| aEdge2Face[aNewEdge] = pE->second; | |
| aEdge2Face.erase(pE); | |
| } | |
| } | |
| } | |
| // Cosine of the maximum angle in triangle (v1,v2,v3) | |
| static float cos_maxangle(const Base::Vector3f& v1, const Base::Vector3f& v2, const Base::Vector3f& v3) | |
| { | |
| float a = Base::Distance(v2, v3); | |
| float b = Base::Distance(v3, v1); | |
| float c = Base::Distance(v1, v2); | |
| float A = a * (b * b + c * c - a * a); | |
| float B = b * (c * c + a * a - b * b); | |
| float C = c * (a * a + b * b - c * c); | |
| return 0.5F * std::min<float>(std::min<float>(A, B), C) / (a * b * c); // min cosine == max angle | |
| } | |
| static float swap_benefit( | |
| const Base::Vector3f& v1, | |
| const Base::Vector3f& v2, | |
| const Base::Vector3f& v3, | |
| const Base::Vector3f& v4 | |
| ) | |
| { | |
| Base::Vector3f n124 = (v4 - v2) % (v1 - v2); | |
| Base::Vector3f n234 = (v3 - v2) % (v4 - v2); | |
| if ((n124 * n234) <= 0.0F) { | |
| return 0.0F; // avoid normal flip | |
| } | |
| return std::max<float>(-cos_maxangle(v1, v2, v3), -cos_maxangle(v1, v3, v4)) | |
| - std::max<float>(-cos_maxangle(v1, v2, v4), -cos_maxangle(v2, v3, v4)); | |
| } | |
| float MeshTopoAlgorithm::SwapEdgeBenefit(FacetIndex f, int e) const | |
| { | |
| const MeshFacetArray& faces = _rclMesh.GetFacets(); | |
| const MeshPointArray& vertices = _rclMesh.GetPoints(); | |
| FacetIndex n = faces[f]._aulNeighbours[e]; | |
| if (n == FACET_INDEX_MAX) { | |
| return 0.0F; // border edge | |
| } | |
| PointIndex v1 = faces[f]._aulPoints[e]; | |
| PointIndex v2 = faces[f]._aulPoints[(e + 1) % 3]; | |
| PointIndex v3 = faces[f]._aulPoints[(e + 2) % 3]; | |
| unsigned short s = faces[n].Side(faces[f]); | |
| if (s == std::numeric_limits<unsigned short>::max()) { | |
| std::cerr << "MeshTopoAlgorithm::SwapEdgeBenefit: error in neighbourhood " | |
| << "of faces " << f << " and " << n << std::endl; | |
| return 0.0F; // topological error | |
| } | |
| PointIndex v4 = faces[n]._aulPoints[(s + 2) % 3]; | |
| if (v3 == v4) { | |
| std::cerr << "MeshTopoAlgorithm::SwapEdgeBenefit: duplicate faces " << f << " and " << n | |
| << std::endl; | |
| return 0.0F; // duplicate faces | |
| } | |
| return swap_benefit(vertices[v2], vertices[v3], vertices[v1], vertices[v4]); | |
| } | |
| using FaceEdge = std::pair<FacetIndex, int>; // (face, edge) pair | |
| using FaceEdgePriority = std::pair<float, FaceEdge>; | |
| void MeshTopoAlgorithm::OptimizeTopology() | |
| { | |
| // Find all edges that can be swapped and insert them into a | |
| // priority queue | |
| const MeshFacetArray& faces = _rclMesh.GetFacets(); | |
| FacetIndex nf = _rclMesh.CountFacets(); | |
| std::priority_queue<FaceEdgePriority> todo; | |
| for (FacetIndex i = 0; i < nf; i++) { | |
| for (int j = 0; j < 3; j++) { | |
| float b = SwapEdgeBenefit(i, j); | |
| if (b > 0.0F) { | |
| todo.push(std::make_pair(b, std::make_pair(i, j))); | |
| } | |
| } | |
| } | |
| // Edges are sorted in decreasing order with respect to their benefit | |
| while (!todo.empty()) { | |
| FacetIndex f = todo.top().second.first; | |
| int e = todo.top().second.second; | |
| todo.pop(); | |
| // Check again if the swap should still be done | |
| if (SwapEdgeBenefit(f, e) <= 0.0F) { | |
| continue; | |
| } | |
| // OK, swap the edge | |
| FacetIndex f2 = faces[f]._aulNeighbours[e]; | |
| SwapEdge(f, f2); | |
| // Insert new edges into queue, if necessary | |
| for (int j = 0; j < 3; j++) { | |
| float b = SwapEdgeBenefit(f, j); | |
| if (b > 0.0F) { | |
| todo.push(std::make_pair(b, std::make_pair(f, j))); | |
| } | |
| } | |
| for (int j = 0; j < 3; j++) { | |
| float b = SwapEdgeBenefit(f2, j); | |
| if (b > 0.0F) { | |
| todo.push(std::make_pair(b, std::make_pair(f2, j))); | |
| } | |
| } | |
| } | |
| } | |
| void MeshTopoAlgorithm::DelaunayFlip(float fMaxAngle) | |
| { | |
| // For each internal edge get the adjacent facets. | |
| std::set<std::pair<FacetIndex, FacetIndex>> aEdge2Face; | |
| FacetIndex index = 0; | |
| for (auto pI = _rclMesh._aclFacetArray.begin(); pI != _rclMesh._aclFacetArray.end(); | |
| ++pI, index++) { | |
| for (FacetIndex nbIndex : pI->_aulNeighbours) { | |
| // ignore open edges | |
| if (nbIndex != FACET_INDEX_MAX) { | |
| FacetIndex ulFt0 = std::min<FacetIndex>(index, nbIndex); | |
| FacetIndex ulFt1 = std::max<FacetIndex>(index, nbIndex); | |
| aEdge2Face.insert(std::pair<FacetIndex, FacetIndex>(ulFt0, ulFt1)); | |
| } | |
| } | |
| } | |
| Base::Vector3f center; | |
| while (!aEdge2Face.empty()) { | |
| std::set<std::pair<FacetIndex, FacetIndex>>::iterator it = aEdge2Face.begin(); | |
| std::pair<FacetIndex, FacetIndex> edge = *it; | |
| aEdge2Face.erase(it); | |
| if (ShouldSwapEdge(edge.first, edge.second, fMaxAngle)) { | |
| float radius = _rclMesh.GetFacet(edge.first).CenterOfCircumCircle(center); | |
| radius *= radius; | |
| const MeshFacet& face_1 = _rclMesh._aclFacetArray[edge.first]; | |
| const MeshFacet& face_2 = _rclMesh._aclFacetArray[edge.second]; | |
| unsigned short side = face_2.Side(edge.first); | |
| MeshPoint vertex = _rclMesh.GetPoint(face_2._aulPoints[(side + 1) % 3]); | |
| if (Base::DistanceP2(center, vertex) < radius) { | |
| SwapEdge(edge.first, edge.second); | |
| for (int i = 0; i < 3; i++) { | |
| if (face_1._aulNeighbours[i] != FACET_INDEX_MAX | |
| && face_1._aulNeighbours[i] != edge.second) { | |
| FacetIndex ulFt0 = std::min<FacetIndex>(edge.first, face_1._aulNeighbours[i]); | |
| FacetIndex ulFt1 = std::max<FacetIndex>(edge.first, face_1._aulNeighbours[i]); | |
| aEdge2Face.insert(std::pair<FacetIndex, FacetIndex>(ulFt0, ulFt1)); | |
| } | |
| if (face_2._aulNeighbours[i] != FACET_INDEX_MAX | |
| && face_2._aulNeighbours[i] != edge.first) { | |
| FacetIndex ulFt0 = std::min<FacetIndex>(edge.second, face_2._aulNeighbours[i]); | |
| FacetIndex ulFt1 = std::max<FacetIndex>(edge.second, face_2._aulNeighbours[i]); | |
| aEdge2Face.insert(std::pair<FacetIndex, FacetIndex>(ulFt0, ulFt1)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| int MeshTopoAlgorithm::DelaunayFlip() | |
| { | |
| int cnt_swap = 0; | |
| _rclMesh._aclFacetArray.ResetFlag(MeshFacet::TMP0); | |
| size_t cnt_facets = _rclMesh._aclFacetArray.size(); | |
| for (size_t i = 0; i < cnt_facets; i++) { | |
| const MeshFacet& f_face = _rclMesh._aclFacetArray[i]; | |
| if (f_face.IsFlag(MeshFacet::TMP0)) { | |
| continue; | |
| } | |
| for (int j = 0; j < 3; j++) { | |
| FacetIndex n = f_face._aulNeighbours[j]; | |
| if (n != FACET_INDEX_MAX) { | |
| const MeshFacet& n_face = _rclMesh._aclFacetArray[n]; | |
| if (n_face.IsFlag(MeshFacet::TMP0)) { | |
| continue; | |
| } | |
| unsigned short k = n_face.Side(f_face); | |
| MeshGeomFacet f1 = _rclMesh.GetFacet(f_face); | |
| MeshGeomFacet f2 = _rclMesh.GetFacet(n_face); | |
| Base::Vector3f c1, c2, p1, p2; | |
| p1 = f1._aclPoints[(j + 2) % 3]; | |
| p2 = f2._aclPoints[(k + 2) % 3]; | |
| float r1 = f1.CenterOfCircumCircle(c1); | |
| r1 = r1 * r1; | |
| float r2 = f2.CenterOfCircumCircle(c2); | |
| r2 = r2 * r2; | |
| float d1 = Base::DistanceP2(c1, p2); | |
| float d2 = Base::DistanceP2(c2, p1); | |
| if (d1 < r1 || d2 < r2) { | |
| SwapEdge(i, n); | |
| cnt_swap++; | |
| f_face.SetFlag(MeshFacet::TMP0); | |
| n_face.SetFlag(MeshFacet::TMP0); | |
| } | |
| } | |
| } | |
| } | |
| return cnt_swap; | |
| } | |
| void MeshTopoAlgorithm::AdjustEdgesToCurvatureDirection() | |
| { | |
| std::vector<Wm4::Vector3<float>> aPnts; | |
| MeshPointIterator cPIt(_rclMesh); | |
| aPnts.reserve(_rclMesh.CountPoints()); | |
| for (cPIt.Init(); cPIt.More(); cPIt.Next()) { | |
| aPnts.emplace_back(cPIt->x, cPIt->y, cPIt->z); | |
| } | |
| // get all point connections | |
| std::vector<int> aIdx; | |
| const MeshFacetArray& raFts = _rclMesh.GetFacets(); | |
| aIdx.reserve(3 * raFts.size()); | |
| // Build map of edges to the referencing facets | |
| FacetIndex k = 0; | |
| std::map<std::pair<PointIndex, PointIndex>, std::list<FacetIndex>> aclEdgeMap; | |
| for (auto jt = raFts.begin(); jt != raFts.end(); ++jt, k++) { | |
| for (int i = 0; i < 3; i++) { | |
| PointIndex ulT0 = jt->_aulPoints[i]; | |
| PointIndex ulT1 = jt->_aulPoints[(i + 1) % 3]; | |
| PointIndex ulP0 = std::min<PointIndex>(ulT0, ulT1); | |
| PointIndex ulP1 = std::max<PointIndex>(ulT0, ulT1); | |
| aclEdgeMap[std::make_pair(ulP0, ulP1)].push_front(k); | |
| aIdx.push_back(static_cast<int>(jt->_aulPoints[i])); | |
| } | |
| } | |
| // compute vertex based curvatures | |
| Wm4::MeshCurvature<float> meshCurv( | |
| static_cast<int>(_rclMesh.CountPoints()), | |
| aPnts.data(), | |
| static_cast<int>(_rclMesh.CountFacets()), | |
| aIdx.data() | |
| ); | |
| // get curvature information now | |
| const Wm4::Vector3<float>* aMaxCurvDir = meshCurv.GetMaxDirections(); | |
| const Wm4::Vector3<float>* aMinCurvDir = meshCurv.GetMinDirections(); | |
| const float* aMaxCurv = meshCurv.GetMaxCurvatures(); | |
| const float* aMinCurv = meshCurv.GetMinCurvatures(); | |
| raFts.ResetFlag(MeshFacet::VISIT); | |
| const MeshPointArray& raPts = _rclMesh.GetPoints(); | |
| for (auto& kt : aclEdgeMap) { | |
| if (kt.second.size() == 2) { | |
| PointIndex uPt1 = kt.first.first; | |
| PointIndex uPt2 = kt.first.second; | |
| FacetIndex uFt1 = kt.second.front(); | |
| FacetIndex uFt2 = kt.second.back(); | |
| const MeshFacet& rFace1 = raFts[uFt1]; | |
| const MeshFacet& rFace2 = raFts[uFt2]; | |
| if (rFace1.IsFlag(MeshFacet::VISIT) || rFace2.IsFlag(MeshFacet::VISIT)) { | |
| continue; | |
| } | |
| PointIndex uPt3 {}, uPt4 {}; | |
| unsigned short side = rFace1.Side(uPt1, uPt2); | |
| uPt3 = rFace1._aulPoints[(side + 2) % 3]; | |
| side = rFace2.Side(uPt1, uPt2); | |
| uPt4 = rFace2._aulPoints[(side + 2) % 3]; | |
| Wm4::Vector3<float> dir; | |
| float fActCurvature {}; | |
| if (std::fabs(aMinCurv[uPt1]) > std::fabs(aMaxCurv[uPt1])) { | |
| fActCurvature = aMinCurv[uPt1]; | |
| dir = aMaxCurvDir[uPt1]; | |
| } | |
| else { | |
| fActCurvature = aMaxCurv[uPt1]; | |
| dir = aMinCurvDir[uPt1]; | |
| } | |
| Base::Vector3f cMinDir(dir.X(), dir.Y(), dir.Z()); | |
| Base::Vector3f cEdgeDir1 = raPts[uPt1] - raPts[uPt2]; | |
| Base::Vector3f cEdgeDir2 = raPts[uPt3] - raPts[uPt4]; | |
| cMinDir.Normalize(); | |
| cEdgeDir1.Normalize(); | |
| cEdgeDir2.Normalize(); | |
| // get the plane and calculate the distance to the fourth point | |
| MeshGeomFacet cPlane(raPts[uPt1], raPts[uPt2], raPts[uPt3]); | |
| // positive or negative distance | |
| float fDist = raPts[uPt4].DistanceToPlane(cPlane._aclPoints[0], cPlane.GetNormal()); | |
| float fLength12 = Base::Distance(raPts[uPt1], raPts[uPt2]); | |
| float fLength34 = Base::Distance(raPts[uPt3], raPts[uPt4]); | |
| if (fabs(cEdgeDir1 * cMinDir) < fabs(cEdgeDir2 * cMinDir)) { | |
| if (IsSwapEdgeLegal(uFt1, uFt2) && fLength34 < 1.05F * fLength12 | |
| && fActCurvature * fDist > 0.0F) { | |
| SwapEdge(uFt1, uFt2); | |
| rFace1.SetFlag(MeshFacet::VISIT); | |
| rFace2.SetFlag(MeshFacet::VISIT); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| bool MeshTopoAlgorithm::InsertVertexAndSwapEdge( | |
| FacetIndex ulFacetPos, | |
| const Base::Vector3f& rclPoint, | |
| float fMaxAngle | |
| ) | |
| { | |
| if (!InsertVertex(ulFacetPos, rclPoint)) { | |
| return false; | |
| } | |
| // get the created elements | |
| FacetIndex ulF1Ind = _rclMesh._aclFacetArray.size() - 2; | |
| FacetIndex ulF2Ind = _rclMesh._aclFacetArray.size() - 1; | |
| MeshFacet& rclF1 = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet& rclF2 = _rclMesh._aclFacetArray[ulF1Ind]; | |
| MeshFacet& rclF3 = _rclMesh._aclFacetArray[ulF2Ind]; | |
| // first facet | |
| for (FacetIndex uNeighbour : rclF1._aulNeighbours) { | |
| if (uNeighbour != FACET_INDEX_MAX && uNeighbour != ulF1Ind && uNeighbour != ulF2Ind) { | |
| if (ShouldSwapEdge(ulFacetPos, uNeighbour, fMaxAngle)) { | |
| SwapEdge(ulFacetPos, uNeighbour); | |
| break; | |
| } | |
| } | |
| } | |
| for (FacetIndex uNeighbour : rclF2._aulNeighbours) { | |
| // second facet | |
| if (uNeighbour != FACET_INDEX_MAX && uNeighbour != ulFacetPos && uNeighbour != ulF2Ind) { | |
| if (ShouldSwapEdge(ulF1Ind, uNeighbour, fMaxAngle)) { | |
| SwapEdge(ulF1Ind, uNeighbour); | |
| break; | |
| } | |
| } | |
| } | |
| // third facet | |
| for (FacetIndex uNeighbour : rclF3._aulNeighbours) { | |
| if (uNeighbour != FACET_INDEX_MAX && uNeighbour != ulFacetPos && uNeighbour != ulF1Ind) { | |
| if (ShouldSwapEdge(ulF2Ind, uNeighbour, fMaxAngle)) { | |
| SwapEdge(ulF2Ind, uNeighbour); | |
| break; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::IsSwapEdgeLegal(FacetIndex ulFacetPos, FacetIndex ulNeighbour) const | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour]; | |
| unsigned short uFSide = rclF.Side(rclN); | |
| unsigned short uNSide = rclN.Side(rclF); | |
| constexpr auto max = std::numeric_limits<unsigned short>::max(); | |
| if (uFSide == max || uNSide == max) { | |
| return false; // not neighbours | |
| } | |
| Base::Vector3f cP1 = _rclMesh._aclPointArray[rclF._aulPoints[uFSide]]; | |
| Base::Vector3f cP2 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 1) % 3]]; | |
| Base::Vector3f cP3 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 2) % 3]]; | |
| Base::Vector3f cP4 = _rclMesh._aclPointArray[rclN._aulPoints[(uNSide + 2) % 3]]; | |
| // do not allow one to create degenerated triangles | |
| MeshGeomFacet cT3(cP4, cP3, cP1); | |
| if (cT3.IsDegenerated(MeshDefinitions::_fMinPointDistanceP2)) { | |
| return false; | |
| } | |
| MeshGeomFacet cT4(cP3, cP4, cP2); | |
| if (cT4.IsDegenerated(MeshDefinitions::_fMinPointDistanceP2)) { | |
| return false; | |
| } | |
| // We must make sure that the two adjacent triangles builds a convex polygon, otherwise | |
| // the swap edge operation is illegal | |
| Base::Vector3f cU = cP2 - cP1; | |
| Base::Vector3f cV = cP4 - cP3; | |
| // build a helper plane through cP1 that must separate cP3 and cP4 | |
| Base::Vector3f cN1 = (cU % cV) % cU; | |
| if (((cP3 - cP1) * cN1) * ((cP4 - cP1) * cN1) >= 0.0F) { | |
| return false; // not convex | |
| } | |
| // build a helper plane through cP3 that must separate cP1 and cP2 | |
| Base::Vector3f cN2 = (cU % cV) % cV; | |
| if (((cP1 - cP3) * cN2) * ((cP2 - cP3) * cN2) >= 0.0F) { | |
| return false; // not convex | |
| } | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::ShouldSwapEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour, float fMaxAngle) const | |
| { | |
| if (!IsSwapEdgeLegal(ulFacetPos, ulNeighbour)) { | |
| return false; | |
| } | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour]; | |
| unsigned short uFSide = rclF.Side(rclN); | |
| unsigned short uNSide = rclN.Side(rclF); | |
| Base::Vector3f cP1 = _rclMesh._aclPointArray[rclF._aulPoints[uFSide]]; | |
| Base::Vector3f cP2 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 1) % 3]]; | |
| Base::Vector3f cP3 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 2) % 3]]; | |
| Base::Vector3f cP4 = _rclMesh._aclPointArray[rclN._aulPoints[(uNSide + 2) % 3]]; | |
| MeshGeomFacet cT1(cP1, cP2, cP3); | |
| float fMax1 = cT1.MaximumAngle(); | |
| MeshGeomFacet cT2(cP2, cP1, cP4); | |
| float fMax2 = cT2.MaximumAngle(); | |
| MeshGeomFacet cT3(cP4, cP3, cP1); | |
| float fMax3 = cT3.MaximumAngle(); | |
| MeshGeomFacet cT4(cP3, cP4, cP2); | |
| float fMax4 = cT4.MaximumAngle(); | |
| // get the angle between the triangles | |
| Base::Vector3f cN1 = cT1.GetNormal(); | |
| Base::Vector3f cN2 = cT2.GetNormal(); | |
| if (cN1.GetAngle(cN2) > fMaxAngle) { | |
| return false; | |
| } | |
| float fMax12 = std::max<float>(fMax1, fMax2); | |
| float fMax34 = std::max<float>(fMax3, fMax4); | |
| return fMax12 > fMax34; | |
| } | |
| void MeshTopoAlgorithm::SwapEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour]; | |
| unsigned short uFSide = rclF.Side(rclN); | |
| unsigned short uNSide = rclN.Side(rclF); | |
| constexpr auto max = std::numeric_limits<unsigned short>::max(); | |
| if (uFSide == max || uNSide == max) { | |
| return; // not neighbours | |
| } | |
| // adjust the neighbourhood | |
| if (rclF._aulNeighbours[(uFSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 1) % 3]].ReplaceNeighbour( | |
| ulFacetPos, | |
| ulNeighbour | |
| ); | |
| } | |
| if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour( | |
| ulNeighbour, | |
| ulFacetPos | |
| ); | |
| } | |
| // swap the point and neighbour indices | |
| rclF._aulPoints[(uFSide + 1) % 3] = rclN._aulPoints[(uNSide + 2) % 3]; | |
| rclN._aulPoints[(uNSide + 1) % 3] = rclF._aulPoints[(uFSide + 2) % 3]; | |
| rclF._aulNeighbours[uFSide] = rclN._aulNeighbours[(uNSide + 1) % 3]; | |
| rclN._aulNeighbours[uNSide] = rclF._aulNeighbours[(uFSide + 1) % 3]; | |
| rclF._aulNeighbours[(uFSide + 1) % 3] = ulNeighbour; | |
| rclN._aulNeighbours[(uNSide + 1) % 3] = ulFacetPos; | |
| } | |
| bool MeshTopoAlgorithm::SplitEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour, const Base::Vector3f& rP) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour]; | |
| unsigned short uFSide = rclF.Side(rclN); | |
| unsigned short uNSide = rclN.Side(rclF); | |
| constexpr auto max = std::numeric_limits<unsigned short>::max(); | |
| if (uFSide == max || uNSide == max) { | |
| return false; // not neighbours | |
| } | |
| PointIndex uPtCnt = _rclMesh._aclPointArray.size(); | |
| PointIndex uPtInd = this->GetOrAddIndex(rP); | |
| FacetIndex ulSize = _rclMesh._aclFacetArray.size(); | |
| // the given point is already part of the mesh => creating new facets would | |
| // be an illegal operation | |
| if (uPtInd < uPtCnt) { | |
| return false; | |
| } | |
| // adjust the neighbourhood | |
| if (rclF._aulNeighbours[(uFSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 1) % 3]].ReplaceNeighbour( | |
| ulFacetPos, | |
| ulSize | |
| ); | |
| } | |
| if (rclN._aulNeighbours[(uNSide + 2) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 2) % 3]].ReplaceNeighbour( | |
| ulNeighbour, | |
| ulSize + 1 | |
| ); | |
| } | |
| MeshFacet cNew1, cNew2; | |
| cNew1._aulPoints[0] = uPtInd; | |
| cNew1._aulPoints[1] = rclF._aulPoints[(uFSide + 1) % 3]; | |
| cNew1._aulPoints[2] = rclF._aulPoints[(uFSide + 2) % 3]; | |
| cNew1._aulNeighbours[0] = ulSize + 1; | |
| cNew1._aulNeighbours[1] = rclF._aulNeighbours[(uFSide + 1) % 3]; | |
| cNew1._aulNeighbours[2] = ulFacetPos; | |
| cNew2._aulPoints[0] = rclN._aulPoints[uNSide]; | |
| cNew2._aulPoints[1] = uPtInd; | |
| cNew2._aulPoints[2] = rclN._aulPoints[(uNSide + 2) % 3]; | |
| cNew2._aulNeighbours[0] = ulSize; | |
| cNew2._aulNeighbours[1] = ulNeighbour; | |
| cNew2._aulNeighbours[2] = rclN._aulNeighbours[(uNSide + 2) % 3]; | |
| // adjust the facets | |
| rclF._aulPoints[(uFSide + 1) % 3] = uPtInd; | |
| rclF._aulNeighbours[(uFSide + 1) % 3] = ulSize; | |
| rclN._aulPoints[uNSide] = uPtInd; | |
| rclN._aulNeighbours[(uNSide + 2) % 3] = ulSize + 1; | |
| // insert new facets | |
| _rclMesh._aclFacetArray.push_back(cNew1); | |
| _rclMesh._aclFacetArray.push_back(cNew2); | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::SplitOpenEdge( | |
| FacetIndex ulFacetPos, | |
| unsigned short uSide, | |
| const Base::Vector3f& rP | |
| ) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| if (rclF._aulNeighbours[uSide] != FACET_INDEX_MAX) { | |
| return false; // not open | |
| } | |
| PointIndex uPtCnt = _rclMesh._aclPointArray.size(); | |
| PointIndex uPtInd = this->GetOrAddIndex(rP); | |
| FacetIndex ulSize = _rclMesh._aclFacetArray.size(); | |
| if (uPtInd < uPtCnt) { | |
| return false; // the given point is already part of the mesh => creating new facets would | |
| // be an illegal operation | |
| } | |
| // adjust the neighbourhood | |
| if (rclF._aulNeighbours[(uSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[(uSide + 1) % 3]].ReplaceNeighbour( | |
| ulFacetPos, | |
| ulSize | |
| ); | |
| } | |
| MeshFacet cNew; | |
| cNew._aulPoints[0] = uPtInd; | |
| cNew._aulPoints[1] = rclF._aulPoints[(uSide + 1) % 3]; | |
| cNew._aulPoints[2] = rclF._aulPoints[(uSide + 2) % 3]; | |
| cNew._aulNeighbours[0] = FACET_INDEX_MAX; | |
| cNew._aulNeighbours[1] = rclF._aulNeighbours[(uSide + 1) % 3]; | |
| cNew._aulNeighbours[2] = ulFacetPos; | |
| // adjust the facets | |
| rclF._aulPoints[(uSide + 1) % 3] = uPtInd; | |
| rclF._aulNeighbours[(uSide + 1) % 3] = ulSize; | |
| // insert new facets | |
| _rclMesh._aclFacetArray.push_back(cNew); | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::Vertex_Less::operator()(const Base::Vector3f& u, const Base::Vector3f& v) const | |
| { | |
| if (std::fabs(u.x - v.x) > std::numeric_limits<float>::epsilon()) { | |
| return u.x < v.x; | |
| } | |
| if (std::fabs(u.y - v.y) > std::numeric_limits<float>::epsilon()) { | |
| return u.y < v.y; | |
| } | |
| if (std::fabs(u.z - v.z) > std::numeric_limits<float>::epsilon()) { | |
| return u.z < v.z; | |
| } | |
| return false; | |
| } | |
| void MeshTopoAlgorithm::BeginCache() | |
| { | |
| delete _cache; | |
| _cache = new tCache(); | |
| PointIndex nbPoints = _rclMesh._aclPointArray.size(); | |
| for (unsigned int pntCpt = 0; pntCpt < nbPoints; ++pntCpt) { | |
| _cache->insert(std::make_pair(_rclMesh._aclPointArray[pntCpt], pntCpt)); | |
| } | |
| } | |
| void MeshTopoAlgorithm::EndCache() | |
| { | |
| if (_cache) { | |
| _cache->clear(); | |
| delete _cache; | |
| _cache = nullptr; | |
| } | |
| } | |
| PointIndex MeshTopoAlgorithm::GetOrAddIndex(const MeshPoint& rclPoint) | |
| { | |
| if (!_cache) { | |
| return _rclMesh._aclPointArray.GetOrAddIndex(rclPoint); | |
| } | |
| unsigned long sz = _rclMesh._aclPointArray.size(); | |
| std::pair<tCache::iterator, bool> retval = _cache->insert(std::make_pair(rclPoint, sz)); | |
| if (retval.second) { | |
| _rclMesh._aclPointArray.push_back(rclPoint); | |
| } | |
| return retval.first->second; | |
| } | |
| std::vector<FacetIndex> MeshTopoAlgorithm::GetFacetsToPoint(FacetIndex uFacetPos, PointIndex uPointPos) const | |
| { | |
| // get all facets this point is referenced by | |
| std::list<FacetIndex> aReference; | |
| aReference.push_back(uFacetPos); | |
| std::set<FacetIndex> aRefFacet; | |
| while (!aReference.empty()) { | |
| FacetIndex uIndex = aReference.front(); | |
| aReference.pop_front(); | |
| aRefFacet.insert(uIndex); | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[uIndex]; | |
| for (int i = 0; i < 3; i++) { | |
| if (rFace._aulPoints[i] == uPointPos) { | |
| if (rFace._aulNeighbours[i] != FACET_INDEX_MAX) { | |
| if (aRefFacet.find(rFace._aulNeighbours[i]) == aRefFacet.end()) { | |
| aReference.push_back(rFace._aulNeighbours[i]); | |
| } | |
| } | |
| if (rFace._aulNeighbours[(i + 2) % 3] != FACET_INDEX_MAX) { | |
| if (aRefFacet.find(rFace._aulNeighbours[(i + 2) % 3]) == aRefFacet.end()) { | |
| aReference.push_back(rFace._aulNeighbours[(i + 2) % 3]); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // copy the items | |
| std::vector<FacetIndex> aRefs; | |
| aRefs.insert(aRefs.end(), aRefFacet.begin(), aRefFacet.end()); | |
| return aRefs; | |
| } | |
| void MeshTopoAlgorithm::Cleanup() | |
| { | |
| _rclMesh.RemoveInvalids(); | |
| _needsCleanup = false; | |
| } | |
| bool MeshTopoAlgorithm::CollapseVertex(const VertexCollapse& vc) | |
| { | |
| if (vc._circumFacets.size() != vc._circumPoints.size()) { | |
| return false; | |
| } | |
| if (vc._circumFacets.size() != 3) { | |
| return false; | |
| } | |
| if (!_rclMesh._aclPointArray[vc._point].IsValid()) { | |
| return false; // the point is marked invalid from a previous run | |
| } | |
| MeshFacet& rFace1 = _rclMesh._aclFacetArray[vc._circumFacets[0]]; | |
| MeshFacet& rFace2 = _rclMesh._aclFacetArray[vc._circumFacets[1]]; | |
| MeshFacet& rFace3 = _rclMesh._aclFacetArray[vc._circumFacets[2]]; | |
| // get the point that is not shared by rFace1 | |
| PointIndex ptIndex = POINT_INDEX_MAX; | |
| std::vector<PointIndex>::const_iterator it; | |
| for (it = vc._circumPoints.begin(); it != vc._circumPoints.end(); ++it) { | |
| if (!rFace1.HasPoint(*it)) { | |
| ptIndex = *it; | |
| break; | |
| } | |
| } | |
| if (ptIndex == POINT_INDEX_MAX) { | |
| return false; | |
| } | |
| FacetIndex neighbour1 = FACET_INDEX_MAX; | |
| FacetIndex neighbour2 = FACET_INDEX_MAX; | |
| const std::vector<FacetIndex>& faces = vc._circumFacets; | |
| // get neighbours that are not part of the faces to be removed | |
| for (int i = 0; i < 3; i++) { | |
| if (std::ranges::find(faces, rFace2._aulNeighbours[i]) == faces.end()) { | |
| neighbour1 = rFace2._aulNeighbours[i]; | |
| } | |
| if (std::ranges::find(faces, rFace3._aulNeighbours[i]) == faces.end()) { | |
| neighbour2 = rFace3._aulNeighbours[i]; | |
| } | |
| } | |
| // adjust point and neighbour indices | |
| rFace1.Transpose(vc._point, ptIndex); | |
| rFace1.ReplaceNeighbour(vc._circumFacets[1], neighbour1); | |
| rFace1.ReplaceNeighbour(vc._circumFacets[2], neighbour2); | |
| if (neighbour1 != FACET_INDEX_MAX) { | |
| MeshFacet& rFace4 = _rclMesh._aclFacetArray[neighbour1]; | |
| rFace4.ReplaceNeighbour(vc._circumFacets[1], vc._circumFacets[0]); | |
| } | |
| if (neighbour2 != FACET_INDEX_MAX) { | |
| MeshFacet& rFace5 = _rclMesh._aclFacetArray[neighbour2]; | |
| rFace5.ReplaceNeighbour(vc._circumFacets[2], vc._circumFacets[0]); | |
| } | |
| // the two facets and the point can be marked for removal | |
| rFace2.SetInvalid(); | |
| rFace3.SetInvalid(); | |
| _rclMesh._aclPointArray[vc._point].SetInvalid(); | |
| _needsCleanup = true; | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::CollapseEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour]; | |
| unsigned short uFSide = rclF.Side(rclN); | |
| unsigned short uNSide = rclN.Side(rclF); | |
| constexpr auto max = std::numeric_limits<unsigned short>::max(); | |
| if (uFSide == max || uNSide == max) { | |
| return false; // not neighbours | |
| } | |
| if (!rclF.IsValid() || !rclN.IsValid()) { | |
| return false; // the facets are marked invalid from a previous run | |
| } | |
| // get the point index we want to remove | |
| PointIndex ulPointPos = rclF._aulPoints[uFSide]; | |
| PointIndex ulPointNew = rclN._aulPoints[uNSide]; | |
| // get all facets this point is referenced by | |
| std::vector<FacetIndex> aRefs = GetFacetsToPoint(ulFacetPos, ulPointPos); | |
| for (FacetIndex it : aRefs) { | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[it]; | |
| rFace.Transpose(ulPointPos, ulPointNew); | |
| } | |
| // set the new neighbourhood | |
| if (rclF._aulNeighbours[(uFSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 1) % 3]].ReplaceNeighbour( | |
| ulFacetPos, | |
| rclF._aulNeighbours[(uFSide + 2) % 3] | |
| ); | |
| } | |
| if (rclF._aulNeighbours[(uFSide + 2) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 2) % 3]].ReplaceNeighbour( | |
| ulFacetPos, | |
| rclF._aulNeighbours[(uFSide + 1) % 3] | |
| ); | |
| } | |
| if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour( | |
| ulNeighbour, | |
| rclN._aulNeighbours[(uNSide + 2) % 3] | |
| ); | |
| } | |
| if (rclN._aulNeighbours[(uNSide + 2) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 2) % 3]].ReplaceNeighbour( | |
| ulNeighbour, | |
| rclN._aulNeighbours[(uNSide + 1) % 3] | |
| ); | |
| } | |
| // isolate the both facets and the point | |
| rclF._aulNeighbours[0] = FACET_INDEX_MAX; | |
| rclF._aulNeighbours[1] = FACET_INDEX_MAX; | |
| rclF._aulNeighbours[2] = FACET_INDEX_MAX; | |
| rclF.SetInvalid(); | |
| rclN._aulNeighbours[0] = FACET_INDEX_MAX; | |
| rclN._aulNeighbours[1] = FACET_INDEX_MAX; | |
| rclN._aulNeighbours[2] = FACET_INDEX_MAX; | |
| rclN.SetInvalid(); | |
| _rclMesh._aclPointArray[ulPointPos].SetInvalid(); | |
| _needsCleanup = true; | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::IsCollapseEdgeLegal(const EdgeCollapse& ec) const | |
| { | |
| // http://stackoverflow.com/a/27049418/148668 | |
| // Check connectivity | |
| // | |
| std::vector<PointIndex> commonPoints; | |
| std::set_intersection( | |
| ec._adjacentFrom.begin(), | |
| ec._adjacentFrom.end(), | |
| ec._adjacentTo.begin(), | |
| ec._adjacentTo.end(), | |
| std::back_insert_iterator<std::vector<PointIndex>>(commonPoints) | |
| ); | |
| if (commonPoints.size() > 2) { | |
| return false; | |
| } | |
| // Check geometry | |
| std::vector<FacetIndex>::const_iterator it; | |
| for (it = ec._changeFacets.begin(); it != ec._changeFacets.end(); ++it) { | |
| MeshFacet f = _rclMesh._aclFacetArray[*it]; | |
| if (!f.IsValid()) { | |
| return false; | |
| } | |
| // ignore the facet(s) at this edge | |
| if (f.HasPoint(ec._fromPoint) && f.HasPoint(ec._toPoint)) { | |
| continue; | |
| } | |
| MeshGeomFacet tria1 = _rclMesh.GetFacet(f); | |
| f.Transpose(ec._fromPoint, ec._toPoint); | |
| MeshGeomFacet tria2 = _rclMesh.GetFacet(f); | |
| if (tria1.GetNormal() * tria2.GetNormal() < 0.0F) { | |
| return false; | |
| } | |
| } | |
| // If the data structure is valid and the algorithm works as expected | |
| // it should never happen to reject the edge-collapse here! | |
| for (it = ec._removeFacets.begin(); it != ec._removeFacets.end(); ++it) { | |
| MeshFacet f = _rclMesh._aclFacetArray[*it]; | |
| if (!f.IsValid()) { | |
| return false; | |
| } | |
| } | |
| if (!_rclMesh._aclPointArray[ec._fromPoint].IsValid()) { | |
| return false; | |
| } | |
| if (!_rclMesh._aclPointArray[ec._toPoint].IsValid()) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::CollapseEdge(const EdgeCollapse& ec) | |
| { | |
| std::vector<FacetIndex>::const_iterator it; | |
| for (it = ec._removeFacets.begin(); it != ec._removeFacets.end(); ++it) { | |
| MeshFacet& f = _rclMesh._aclFacetArray[*it]; | |
| f.SetInvalid(); | |
| // adjust the neighbourhood | |
| std::vector<FacetIndex> neighbours; | |
| for (FacetIndex nbIndex : f._aulNeighbours) { | |
| // get the neighbours of the facet that won't be invalidated | |
| if (nbIndex != FACET_INDEX_MAX) { | |
| if (std::ranges::find(ec._removeFacets, nbIndex) == ec._removeFacets.end()) { | |
| neighbours.push_back(nbIndex); | |
| } | |
| } | |
| } | |
| if (neighbours.size() == 2) { | |
| MeshFacet& n1 = _rclMesh._aclFacetArray[neighbours[0]]; | |
| n1.ReplaceNeighbour(*it, neighbours[1]); | |
| MeshFacet& n2 = _rclMesh._aclFacetArray[neighbours[1]]; | |
| n2.ReplaceNeighbour(*it, neighbours[0]); | |
| } | |
| else if (neighbours.size() == 1) { | |
| MeshFacet& n1 = _rclMesh._aclFacetArray[neighbours[0]]; | |
| n1.ReplaceNeighbour(*it, FACET_INDEX_MAX); | |
| } | |
| } | |
| for (it = ec._changeFacets.begin(); it != ec._changeFacets.end(); ++it) { | |
| MeshFacet& f = _rclMesh._aclFacetArray[*it]; | |
| f.Transpose(ec._fromPoint, ec._toPoint); | |
| } | |
| _rclMesh._aclPointArray[ec._fromPoint].SetInvalid(); | |
| _needsCleanup = true; | |
| return true; | |
| } | |
| bool MeshTopoAlgorithm::CollapseFacet(FacetIndex ulFacetPos) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| if (!rclF.IsValid()) { | |
| return false; // the facet is marked invalid from a previous run | |
| } | |
| // get the point index we want to remove | |
| PointIndex ulPointInd0 = rclF._aulPoints[0]; | |
| PointIndex ulPointInd1 = rclF._aulPoints[1]; | |
| PointIndex ulPointInd2 = rclF._aulPoints[2]; | |
| // move the vertex to the gravity center | |
| Base::Vector3f cCenter = _rclMesh.GetGravityPoint(rclF); | |
| _rclMesh._aclPointArray[ulPointInd0] = cCenter; | |
| // set the new point indices for all facets that share one of the points to be deleted | |
| std::vector<FacetIndex> aRefs = GetFacetsToPoint(ulFacetPos, ulPointInd1); | |
| for (FacetIndex it : aRefs) { | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[it]; | |
| rFace.Transpose(ulPointInd1, ulPointInd0); | |
| } | |
| aRefs = GetFacetsToPoint(ulFacetPos, ulPointInd2); | |
| for (FacetIndex it : aRefs) { | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[it]; | |
| rFace.Transpose(ulPointInd2, ulPointInd0); | |
| } | |
| // set the neighbourhood of the circumjacent facets | |
| for (FacetIndex nbIndex : rclF._aulNeighbours) { | |
| if (nbIndex == FACET_INDEX_MAX) { | |
| continue; | |
| } | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[nbIndex]; | |
| unsigned short uNSide = rclN.Side(rclF); | |
| if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour( | |
| nbIndex, | |
| rclN._aulNeighbours[(uNSide + 2) % 3] | |
| ); | |
| } | |
| if (rclN._aulNeighbours[(uNSide + 2) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 2) % 3]].ReplaceNeighbour( | |
| nbIndex, | |
| rclN._aulNeighbours[(uNSide + 1) % 3] | |
| ); | |
| } | |
| // Isolate the neighbours from the topology | |
| rclN._aulNeighbours[0] = FACET_INDEX_MAX; | |
| rclN._aulNeighbours[1] = FACET_INDEX_MAX; | |
| rclN._aulNeighbours[2] = FACET_INDEX_MAX; | |
| rclN.SetInvalid(); | |
| } | |
| // Isolate this facet and make two of its points invalid | |
| rclF._aulNeighbours[0] = FACET_INDEX_MAX; | |
| rclF._aulNeighbours[1] = FACET_INDEX_MAX; | |
| rclF._aulNeighbours[2] = FACET_INDEX_MAX; | |
| rclF.SetInvalid(); | |
| _rclMesh._aclPointArray[ulPointInd1].SetInvalid(); | |
| _rclMesh._aclPointArray[ulPointInd2].SetInvalid(); | |
| _needsCleanup = true; | |
| return true; | |
| } | |
| void MeshTopoAlgorithm::SplitFacet( | |
| FacetIndex ulFacetPos, | |
| const Base::Vector3f& rP1, | |
| const Base::Vector3f& rP2 | |
| ) | |
| { | |
| float fEps = MESH_MIN_EDGE_LEN; | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; | |
| MeshPoint& rVertex0 = _rclMesh._aclPointArray[rFace._aulPoints[0]]; | |
| MeshPoint& rVertex1 = _rclMesh._aclPointArray[rFace._aulPoints[1]]; | |
| MeshPoint& rVertex2 = _rclMesh._aclPointArray[rFace._aulPoints[2]]; | |
| auto pointIndex = [=](const Base::Vector3f& rP) { | |
| unsigned short equalP = std::numeric_limits<unsigned short>::max(); | |
| if (Base::Distance(rVertex0, rP) < fEps) { | |
| equalP = 0; | |
| } | |
| else if (Base::Distance(rVertex1, rP) < fEps) { | |
| equalP = 1; | |
| } | |
| else if (Base::Distance(rVertex2, rP) < fEps) { | |
| equalP = 2; | |
| } | |
| return equalP; | |
| }; | |
| unsigned short equalP1 = pointIndex(rP1); | |
| unsigned short equalP2 = pointIndex(rP2); | |
| constexpr auto max = std::numeric_limits<unsigned short>::max(); | |
| // both points are coincident with the corner points | |
| if (equalP1 != max && equalP2 != max) { | |
| return; // must not split the facet | |
| } | |
| if (equalP1 != max) { | |
| // get the edge to the second given point and perform a split edge operation | |
| SplitFacetOnOneEdge(ulFacetPos, rP2); | |
| } | |
| else if (equalP2 != max) { | |
| // get the edge to the first given point and perform a split edge operation | |
| SplitFacetOnOneEdge(ulFacetPos, rP1); | |
| } | |
| else { | |
| SplitFacetOnTwoEdges(ulFacetPos, rP1, rP2); | |
| } | |
| } | |
| void MeshTopoAlgorithm::SplitFacetOnOneEdge(FacetIndex ulFacetPos, const Base::Vector3f& rP) | |
| { | |
| float fMinDist = std::numeric_limits<float>::max(); | |
| unsigned short iEdgeNo = std::numeric_limits<unsigned short>::max(); | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; | |
| for (unsigned short i = 0; i < 3; i++) { | |
| Base::Vector3f cBase(_rclMesh._aclPointArray[rFace._aulPoints[i]]); | |
| Base::Vector3f cEnd(_rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]]); | |
| Base::Vector3f cDir = cEnd - cBase; | |
| float fDist = rP.DistanceToLine(cBase, cDir); | |
| if (fDist < fMinDist) { | |
| fMinDist = fDist; | |
| iEdgeNo = i; | |
| } | |
| } | |
| if (fMinDist < 0.05F) { | |
| if (rFace._aulNeighbours[iEdgeNo] != FACET_INDEX_MAX) { | |
| SplitEdge(ulFacetPos, rFace._aulNeighbours[iEdgeNo], rP); | |
| } | |
| else { | |
| SplitOpenEdge(ulFacetPos, iEdgeNo, rP); | |
| } | |
| } | |
| } | |
| void MeshTopoAlgorithm::SplitFacetOnTwoEdges( | |
| FacetIndex ulFacetPos, | |
| const Base::Vector3f& rP1, | |
| const Base::Vector3f& rP2 | |
| ) | |
| { | |
| // search for the matching edges | |
| unsigned short iEdgeNo1 = std::numeric_limits<unsigned short>::max(); | |
| unsigned short iEdgeNo2 = std::numeric_limits<unsigned short>::max(); | |
| float fMinDist1 = std::numeric_limits<float>::max(); | |
| float fMinDist2 = std::numeric_limits<float>::max(); | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; | |
| for (unsigned short i = 0; i < 3; i++) { | |
| Base::Vector3f cBase(_rclMesh._aclPointArray[rFace._aulPoints[i]]); | |
| Base::Vector3f cEnd(_rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]]); | |
| Base::Vector3f cDir = cEnd - cBase; | |
| float fDist = rP1.DistanceToLine(cBase, cDir); | |
| if (fDist < fMinDist1) { | |
| fMinDist1 = fDist; | |
| iEdgeNo1 = i; | |
| } | |
| fDist = rP2.DistanceToLine(cBase, cDir); | |
| if (fDist < fMinDist2) { | |
| fMinDist2 = fDist; | |
| iEdgeNo2 = i; | |
| } | |
| } | |
| if (iEdgeNo1 == iEdgeNo2 || fMinDist1 >= 0.05F || fMinDist2 >= 0.05F) { | |
| return; // no valid configuration | |
| } | |
| // make first point lying on the previous edge | |
| Base::Vector3f cP1 = rP1; | |
| Base::Vector3f cP2 = rP2; | |
| if ((iEdgeNo2 + 1) % 3 == iEdgeNo1) { | |
| std::swap(iEdgeNo1, iEdgeNo2); | |
| std::swap(cP1, cP2); | |
| } | |
| // insert new points | |
| PointIndex cntPts1 = this->GetOrAddIndex(cP1); | |
| PointIndex cntPts2 = this->GetOrAddIndex(cP2); | |
| FacetIndex cntFts = _rclMesh.CountFacets(); | |
| unsigned short v0 = (iEdgeNo2 + 1) % 3; | |
| unsigned short v1 = iEdgeNo1; | |
| unsigned short v2 = iEdgeNo2; | |
| PointIndex p0 = rFace._aulPoints[v0]; | |
| PointIndex p1 = rFace._aulPoints[v1]; | |
| PointIndex p2 = rFace._aulPoints[v2]; | |
| FacetIndex n0 = rFace._aulNeighbours[v0]; | |
| FacetIndex n1 = rFace._aulNeighbours[v1]; | |
| FacetIndex n2 = rFace._aulNeighbours[v2]; | |
| // Modify and add facets | |
| // | |
| rFace._aulPoints[v0] = cntPts2; | |
| rFace._aulPoints[v1] = cntPts1; | |
| rFace._aulNeighbours[v0] = cntFts + 1; | |
| float dist1 = Base::DistanceP2(_rclMesh._aclPointArray[p0], cP1); | |
| float dist2 = Base::DistanceP2(_rclMesh._aclPointArray[p1], cP2); | |
| if (dist1 > dist2) { | |
| AddFacet(p0, p1, cntPts2, n0, cntFts + 1, n2); | |
| AddFacet(p1, cntPts1, cntPts2, n1, ulFacetPos, cntFts); | |
| } | |
| else { | |
| AddFacet(p0, p1, cntPts1, n0, n1, cntFts + 1); | |
| AddFacet(p0, cntPts1, cntPts2, cntFts, ulFacetPos, n2); | |
| } | |
| std::vector<FacetIndex> fixIndices; | |
| fixIndices.push_back(ulFacetPos); | |
| if (n0 != FACET_INDEX_MAX) { | |
| fixIndices.push_back(n0); | |
| } | |
| // split up the neighbour facets | |
| if (n1 != FACET_INDEX_MAX) { | |
| fixIndices.push_back(n1); | |
| MeshFacet& rN = _rclMesh._aclFacetArray[n1]; | |
| for (FacetIndex nbIndex : rN._aulNeighbours) { | |
| fixIndices.push_back(nbIndex); | |
| } | |
| SplitFacet(n1, p1, p2, cntPts1); | |
| } | |
| if (n2 != FACET_INDEX_MAX) { | |
| fixIndices.push_back(n2); | |
| MeshFacet& rN = _rclMesh._aclFacetArray[n2]; | |
| for (FacetIndex nbIndex : rN._aulNeighbours) { | |
| fixIndices.push_back(nbIndex); | |
| } | |
| SplitFacet(n2, p2, p0, cntPts2); | |
| } | |
| FacetIndex cntFts2 = _rclMesh.CountFacets(); | |
| for (FacetIndex i = cntFts; i < cntFts2; i++) { | |
| fixIndices.push_back(i); | |
| } | |
| std::sort(fixIndices.begin(), fixIndices.end()); | |
| fixIndices.erase(std::unique(fixIndices.begin(), fixIndices.end()), fixIndices.end()); | |
| HarmonizeNeighbours(fixIndices); | |
| } | |
| void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos, PointIndex P1, PointIndex P2, PointIndex Pn) | |
| { | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos]; | |
| unsigned short side = rFace.Side(P1, P2); | |
| if (side != std::numeric_limits<unsigned short>::max()) { | |
| PointIndex V1 = rFace._aulPoints[(side + 1) % 3]; | |
| PointIndex V2 = rFace._aulPoints[(side + 2) % 3]; | |
| FacetIndex size = _rclMesh._aclFacetArray.size(); | |
| rFace._aulPoints[(side + 1) % 3] = Pn; | |
| FacetIndex N1 = rFace._aulNeighbours[(side + 1) % 3]; | |
| if (N1 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[N1].ReplaceNeighbour(ulFacetPos, size); | |
| } | |
| rFace._aulNeighbours[(side + 1) % 3] = ulFacetPos; | |
| AddFacet(Pn, V1, V2); | |
| } | |
| } | |
| void MeshTopoAlgorithm::AddFacet(PointIndex P1, PointIndex P2, PointIndex P3) | |
| { | |
| MeshFacet facet; | |
| facet._aulPoints[0] = P1; | |
| facet._aulPoints[1] = P2; | |
| facet._aulPoints[2] = P3; | |
| _rclMesh._aclFacetArray.push_back(facet); | |
| } | |
| void MeshTopoAlgorithm::AddFacet( | |
| PointIndex P1, | |
| PointIndex P2, | |
| PointIndex P3, | |
| FacetIndex N1, | |
| FacetIndex N2, | |
| FacetIndex N3 | |
| ) | |
| { | |
| MeshFacet facet; | |
| facet._aulPoints[0] = P1; | |
| facet._aulPoints[1] = P2; | |
| facet._aulPoints[2] = P3; | |
| facet._aulNeighbours[0] = N1; | |
| facet._aulNeighbours[1] = N2; | |
| facet._aulNeighbours[2] = N3; | |
| _rclMesh._aclFacetArray.push_back(facet); | |
| } | |
| void MeshTopoAlgorithm::HarmonizeNeighbours(const std::vector<FacetIndex>& ulFacets) | |
| { | |
| for (FacetIndex it : ulFacets) { | |
| for (FacetIndex jt : ulFacets) { | |
| HarmonizeNeighbours(it, jt); | |
| } | |
| } | |
| } | |
| void MeshTopoAlgorithm::HarmonizeNeighbours(FacetIndex facet1, FacetIndex facet2) | |
| { | |
| if (facet1 == facet2) { | |
| return; | |
| } | |
| MeshFacet& rFace1 = _rclMesh._aclFacetArray[facet1]; | |
| MeshFacet& rFace2 = _rclMesh._aclFacetArray[facet2]; | |
| unsigned short side = rFace1.Side(rFace2); | |
| if (side != std::numeric_limits<unsigned short>::max()) { | |
| rFace1._aulNeighbours[side] = facet2; | |
| } | |
| side = rFace2.Side(rFace1); | |
| if (side != std::numeric_limits<unsigned short>::max()) { | |
| rFace2._aulNeighbours[side] = facet1; | |
| } | |
| } | |
| void MeshTopoAlgorithm::SplitNeighbourFacet( | |
| FacetIndex ulFacetPos, | |
| unsigned short uFSide, | |
| const Base::Vector3f& rPoint | |
| ) | |
| { | |
| MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos]; | |
| FacetIndex ulNeighbour = rclF._aulNeighbours[uFSide]; | |
| MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour]; | |
| unsigned short uNSide = rclN.Side(rclF); | |
| PointIndex uPtInd = this->GetOrAddIndex(rPoint); | |
| FacetIndex ulSize = _rclMesh._aclFacetArray.size(); | |
| // adjust the neighbourhood | |
| if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour( | |
| ulNeighbour, | |
| ulSize | |
| ); | |
| } | |
| MeshFacet cNew; | |
| cNew._aulPoints[0] = uPtInd; | |
| cNew._aulPoints[1] = rclN._aulPoints[(uNSide + 1) % 3]; | |
| cNew._aulPoints[2] = rclN._aulPoints[(uNSide + 2) % 3]; | |
| cNew._aulNeighbours[0] = ulFacetPos; | |
| cNew._aulNeighbours[1] = rclN._aulNeighbours[(uNSide + 1) % 3]; | |
| cNew._aulNeighbours[2] = ulNeighbour; | |
| // adjust the facet | |
| rclN._aulPoints[(uNSide + 1) % 3] = uPtInd; | |
| rclN._aulNeighbours[(uNSide + 1) % 3] = ulSize; | |
| // insert new facet | |
| _rclMesh._aclFacetArray.push_back(cNew); | |
| } | |
| // create 3 new facets | |
| MeshGeomFacet clFacet; | |
| // facet [P1, Ei+1, P2] | |
| clFacet._aclPoints[0] = cP1; | |
| clFacet._aclPoints[1] = _rclMesh._aclPointArray[rFace._aulPoints[(iEdgeNo1+1)%3]]; | |
| clFacet._aclPoints[2] = cP2; | |
| clFacet.CalcNormal(); | |
| _aclNewFacets.push_back(clFacet); | |
| // facet [P2, Ei+2, Ei] | |
| clFacet._aclPoints[0] = cP2; | |
| clFacet._aclPoints[1] = _rclMesh._aclPointArray[rFace._aulPoints[(iEdgeNo1+2)%3]]; | |
| clFacet._aclPoints[2] = _rclMesh._aclPointArray[rFace._aulPoints[iEdgeNo1]]; | |
| clFacet.CalcNormal(); | |
| _aclNewFacets.push_back(clFacet); | |
| // facet [P2, Ei, P1] | |
| clFacet._aclPoints[0] = cP2; | |
| clFacet._aclPoints[1] = _rclMesh._aclPointArray[rFace._aulPoints[iEdgeNo1]]; | |
| clFacet._aclPoints[2] = cP1; | |
| clFacet.CalcNormal(); | |
| _aclNewFacets.push_back(clFacet); | |
| bool MeshTopoAlgorithm::RemoveDegeneratedFacet(FacetIndex index) | |
| { | |
| if (index >= _rclMesh._aclFacetArray.size()) { | |
| return false; | |
| } | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[index]; | |
| // coincident corners (either topological or geometrical) | |
| for (int i = 0; i < 3; i++) { | |
| const MeshPoint& rE0 = _rclMesh._aclPointArray[rFace._aulPoints[i]]; | |
| const MeshPoint& rE1 = _rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]]; | |
| if (rE0 == rE1) { | |
| FacetIndex uN1 = rFace._aulNeighbours[(i + 1) % 3]; | |
| FacetIndex uN2 = rFace._aulNeighbours[(i + 2) % 3]; | |
| if (uN2 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[uN2].ReplaceNeighbour(index, uN1); | |
| } | |
| if (uN1 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[uN1].ReplaceNeighbour(index, uN2); | |
| } | |
| // isolate the face and remove it | |
| rFace._aulNeighbours[0] = FACET_INDEX_MAX; | |
| rFace._aulNeighbours[1] = FACET_INDEX_MAX; | |
| rFace._aulNeighbours[2] = FACET_INDEX_MAX; | |
| _rclMesh.DeleteFacet(index); | |
| return true; | |
| } | |
| } | |
| // We have a facet of the form | |
| // P0 +----+------+P2 | |
| // P1 | |
| for (int j = 0; j < 3; j++) { | |
| Base::Vector3f cVec1 = _rclMesh._aclPointArray[rFace._aulPoints[(j + 1) % 3]] | |
| - _rclMesh._aclPointArray[rFace._aulPoints[j]]; | |
| Base::Vector3f cVec2 = _rclMesh._aclPointArray[rFace._aulPoints[(j + 2) % 3]] | |
| - _rclMesh._aclPointArray[rFace._aulPoints[j]]; | |
| // adjust the neighbourhoods and point indices | |
| if (cVec1 * cVec2 < 0.0F) { | |
| FacetIndex uN1 = rFace._aulNeighbours[(j + 1) % 3]; | |
| if (uN1 != FACET_INDEX_MAX) { | |
| // get the neighbour and common edge side | |
| MeshFacet& rNb = _rclMesh._aclFacetArray[uN1]; | |
| unsigned short side = rNb.Side(index); | |
| // bend the point indices | |
| rFace._aulPoints[(j + 2) % 3] = rNb._aulPoints[(side + 2) % 3]; | |
| rNb._aulPoints[(side + 1) % 3] = rFace._aulPoints[j]; | |
| // set correct neighbourhood | |
| FacetIndex uN2 = rFace._aulNeighbours[(j + 2) % 3]; | |
| rNb._aulNeighbours[side] = uN2; | |
| if (uN2 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[uN2].ReplaceNeighbour(index, uN1); | |
| } | |
| FacetIndex uN3 = rNb._aulNeighbours[(side + 1) % 3]; | |
| rFace._aulNeighbours[(j + 1) % 3] = uN3; | |
| if (uN3 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[uN3].ReplaceNeighbour(uN1, index); | |
| } | |
| rNb._aulNeighbours[(side + 1) % 3] = index; | |
| rFace._aulNeighbours[(j + 2) % 3] = uN1; | |
| } | |
| else { | |
| _rclMesh.DeleteFacet(index); | |
| } | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool MeshTopoAlgorithm::RemoveCorruptedFacet(FacetIndex index) | |
| { | |
| if (index >= _rclMesh._aclFacetArray.size()) { | |
| return false; | |
| } | |
| MeshFacet& rFace = _rclMesh._aclFacetArray[index]; | |
| // coincident corners (topological) | |
| for (int i = 0; i < 3; i++) { | |
| if (rFace._aulPoints[i] == rFace._aulPoints[(i + 1) % 3]) { | |
| FacetIndex uN1 = rFace._aulNeighbours[(i + 1) % 3]; | |
| FacetIndex uN2 = rFace._aulNeighbours[(i + 2) % 3]; | |
| if (uN2 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[uN2].ReplaceNeighbour(index, uN1); | |
| } | |
| if (uN1 != FACET_INDEX_MAX) { | |
| _rclMesh._aclFacetArray[uN1].ReplaceNeighbour(index, uN2); | |
| } | |
| // isolate the face and remove it | |
| rFace._aulNeighbours[0] = FACET_INDEX_MAX; | |
| rFace._aulNeighbours[1] = FACET_INDEX_MAX; | |
| rFace._aulNeighbours[2] = FACET_INDEX_MAX; | |
| _rclMesh.DeleteFacet(index); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void MeshTopoAlgorithm::FillupHoles( | |
| unsigned long length, | |
| int level, | |
| AbstractPolygonTriangulator& cTria, | |
| std::list<std::vector<PointIndex>>& aFailed | |
| ) | |
| { | |
| // get the mesh boundaries as an array of point indices | |
| std::list<std::vector<PointIndex>> aBorders, aFillBorders; | |
| MeshAlgorithm cAlgo(_rclMesh); | |
| cAlgo.GetMeshBorders(aBorders); | |
| // split boundary loops if needed | |
| cAlgo.SplitBoundaryLoops(aBorders); | |
| for (const auto& aBorder : aBorders) { | |
| if (aBorder.size() - 1 <= length) { // ignore boundary with too many edges | |
| aFillBorders.push_back(aBorder); | |
| } | |
| } | |
| if (!aFillBorders.empty()) { | |
| FillupHoles(level, cTria, aFillBorders, aFailed); | |
| } | |
| } | |
| void MeshTopoAlgorithm::FillupHoles( | |
| int level, | |
| AbstractPolygonTriangulator& cTria, | |
| const std::list<std::vector<PointIndex>>& aBorders, | |
| std::list<std::vector<PointIndex>>& aFailed | |
| ) | |
| { | |
| // get the facets to a point | |
| MeshRefPointToFacets cPt2Fac(_rclMesh); | |
| MeshAlgorithm cAlgo(_rclMesh); | |
| MeshFacetArray newFacets; | |
| MeshPointArray newPoints; | |
| unsigned long numberOfOldPoints = _rclMesh._aclPointArray.size(); | |
| for (const auto& aBorder : aBorders) { | |
| MeshFacetArray cFacets; | |
| MeshPointArray cPoints; | |
| std::vector<PointIndex> bound = aBorder; | |
| if (cAlgo.FillupHole(bound, cTria, cFacets, cPoints, level, &cPt2Fac)) { | |
| if (bound.front() == bound.back()) { | |
| bound.pop_back(); | |
| } | |
| // the triangulation may produce additional points which we must take into account when | |
| // appending to the mesh | |
| if (cPoints.size() > bound.size()) { | |
| unsigned long countBoundaryPoints = bound.size(); | |
| unsigned long countDifference = cPoints.size() - countBoundaryPoints; | |
| MeshPointArray::_TIterator pt = cPoints.begin() + countBoundaryPoints; | |
| for (unsigned long i = 0; i < countDifference; i++, pt++) { | |
| bound.push_back(numberOfOldPoints++); | |
| newPoints.push_back(*pt); | |
| } | |
| } | |
| if (cTria.NeedsReindexing()) { | |
| for (auto& cFacet : cFacets) { | |
| cFacet._aulPoints[0] = bound[cFacet._aulPoints[0]]; | |
| cFacet._aulPoints[1] = bound[cFacet._aulPoints[1]]; | |
| cFacet._aulPoints[2] = bound[cFacet._aulPoints[2]]; | |
| newFacets.push_back(cFacet); | |
| } | |
| } | |
| else { | |
| for (auto& cFacet : cFacets) { | |
| newFacets.push_back(cFacet); | |
| } | |
| } | |
| } | |
| else { | |
| aFailed.push_back(aBorder); | |
| } | |
| } | |
| // insert new points and faces into the mesh structure | |
| _rclMesh._aclPointArray.insert(_rclMesh._aclPointArray.end(), newPoints.begin(), newPoints.end()); | |
| for (const auto& newPoint : newPoints) { | |
| _rclMesh._clBoundBox.Add(newPoint); | |
| } | |
| if (!newFacets.empty()) { | |
| // Do some checks for invalid point indices | |
| MeshFacetArray addFacets; | |
| addFacets.reserve(newFacets.size()); | |
| unsigned long ctPoints = _rclMesh.CountPoints(); | |
| for (auto& newFacet : newFacets) { | |
| if (newFacet._aulPoints[0] >= ctPoints || newFacet._aulPoints[1] >= ctPoints | |
| || newFacet._aulPoints[2] >= ctPoints) { | |
| Base::Console().log( | |
| "Ignore invalid face <%d, %d, %d> (%d vertices)\n", | |
| newFacet._aulPoints[0], | |
| newFacet._aulPoints[1], | |
| newFacet._aulPoints[2], | |
| ctPoints | |
| ); | |
| } | |
| else { | |
| addFacets.push_back(newFacet); | |
| } | |
| } | |
| _rclMesh.AddFacets(addFacets, true); | |
| } | |
| } | |
| void MeshTopoAlgorithm::FindHoles(unsigned long length, std::list<std::vector<PointIndex>>& aBorders) const | |
| { | |
| std::list<std::vector<PointIndex>> border; | |
| MeshAlgorithm cAlgo(_rclMesh); | |
| cAlgo.GetMeshBorders(border); | |
| for (const auto& it : border) { | |
| if (it.size() <= length) { | |
| aBorders.push_back(it); | |
| } | |
| } | |
| } | |
| void MeshTopoAlgorithm::FindComponents(unsigned long count, std::vector<FacetIndex>& findIndices) | |
| { | |
| std::vector<std::vector<FacetIndex>> segments; | |
| MeshComponents comp(_rclMesh); | |
| comp.SearchForComponents(MeshComponents::OverEdge, segments); | |
| for (const auto& segment : segments) { | |
| if (segment.size() <= count) { | |
| findIndices.insert(findIndices.end(), segment.begin(), segment.end()); | |
| } | |
| } | |
| } | |
| void MeshTopoAlgorithm::RemoveComponents(unsigned long count) | |
| { | |
| std::vector<FacetIndex> removeFacets; | |
| FindComponents(count, removeFacets); | |
| if (!removeFacets.empty()) { | |
| _rclMesh.DeleteFacets(removeFacets); | |
| } | |
| } | |
| void MeshTopoAlgorithm::HarmonizeNormals() | |
| { | |
| std::vector<FacetIndex> uIndices = MeshEvalOrientation(_rclMesh).GetIndices(); | |
| for (FacetIndex index : uIndices) { | |
| _rclMesh._aclFacetArray[index].FlipNormal(); | |
| } | |
| } | |
| void MeshTopoAlgorithm::FlipNormals() | |
| { | |
| for (auto i = _rclMesh._aclFacetArray.begin(); i < _rclMesh._aclFacetArray.end(); ++i) { | |
| i->FlipNormal(); | |
| } | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Some important formulas: | |
| * | |
| * Ne = 3Nv - Nb + 3B + 6(G-R) | |
| * Nt = 2Nv - Nb + 2B + 4(G-R) | |
| * | |
| * Ne <= 3Nv + 6(G-R) | |
| * Nt <= 2Nv + 4(G-R) | |
| * | |
| * Ne ~ 3Nv, Nv >> G, Nv >> R | |
| * Nt ~ 2Nv, Nv >> G, Nv >> R | |
| * | |
| * Ne = #Edges | |
| * Nt = #Facets | |
| * Nv = #Vertices | |
| * Nb = #Boundary vertices | |
| * B = #Boundaries | |
| * G = Genus (Number of holes) | |
| * R = #components | |
| * | |
| * See also http://max-limper.de/publications/Euler/ | |
| */ | |
| MeshComponents::MeshComponents(const MeshKernel& rclMesh) | |
| : _rclMesh(rclMesh) | |
| {} | |
| void MeshComponents::SearchForComponents(TMode tMode, std::vector<std::vector<FacetIndex>>& aclT) const | |
| { | |
| // all facets | |
| std::vector<FacetIndex> aulAllFacets(_rclMesh.CountFacets()); | |
| FacetIndex k = 0; | |
| for (FacetIndex& aulAllFacet : aulAllFacets) { | |
| aulAllFacet = k++; | |
| } | |
| SearchForComponents(tMode, aulAllFacets, aclT); | |
| } | |
| void MeshComponents::SearchForComponents( | |
| TMode tMode, | |
| const std::vector<FacetIndex>& aSegment, | |
| std::vector<std::vector<FacetIndex>>& aclT | |
| ) const | |
| { | |
| FacetIndex ulStartFacet {}; | |
| if (_rclMesh.CountFacets() == 0) { | |
| return; | |
| } | |
| // reset VISIT flags | |
| MeshAlgorithm cAlgo(_rclMesh); | |
| cAlgo.SetFacetFlag(MeshFacet::VISIT); | |
| cAlgo.ResetFacetsFlag(aSegment, MeshFacet::VISIT); | |
| const MeshFacetArray& rFAry = _rclMesh.GetFacets(); | |
| MeshFacetArray::_TConstIterator iTri = rFAry.begin(); | |
| MeshFacetArray::_TConstIterator iBeg = rFAry.begin(); | |
| MeshFacetArray::_TConstIterator iEnd = rFAry.end(); | |
| // start from the first not visited facet | |
| unsigned long ulVisited = cAlgo.CountFacetFlag(MeshFacet::VISIT); | |
| MeshIsNotFlag<MeshFacet> flag; | |
| iTri = std::find_if(iTri, iEnd, [flag](const MeshFacet& f) { return flag(f, MeshFacet::VISIT); }); | |
| ulStartFacet = iTri - iBeg; | |
| // visitor | |
| std::vector<FacetIndex> aclComponent; | |
| std::vector<std::vector<FacetIndex>> aclConnectComp; | |
| MeshTopFacetVisitor clFVisitor(aclComponent); | |
| while (ulStartFacet != FACET_INDEX_MAX) { | |
| // collect all facets of a component | |
| aclComponent.clear(); | |
| if (tMode == OverEdge) { | |
| ulVisited += _rclMesh.VisitNeighbourFacets(clFVisitor, ulStartFacet); | |
| } | |
| else if (tMode == OverPoint) { | |
| ulVisited += _rclMesh.VisitNeighbourFacetsOverCorners(clFVisitor, ulStartFacet); | |
| } | |
| // get also start facet | |
| aclComponent.push_back(ulStartFacet); | |
| aclConnectComp.push_back(aclComponent); | |
| // if the mesh consists of several topologic independent components | |
| // We can search from position 'iTri' on because all elements _before_ are already visited | |
| // what we know from the previous iteration. | |
| iTri = std::find_if(iTri, iEnd, [flag](const MeshFacet& f) { | |
| return flag(f, MeshFacet::VISIT); | |
| }); | |
| if (iTri < iEnd) { | |
| ulStartFacet = iTri - iBeg; | |
| } | |
| else { | |
| ulStartFacet = FACET_INDEX_MAX; | |
| } | |
| } | |
| // sort components by size (descending order) | |
| std::sort(aclConnectComp.begin(), aclConnectComp.end(), CNofFacetsCompare()); | |
| aclT = aclConnectComp; | |
| boost::ignore_unused(ulVisited); | |
| } | |