Download src/Mod/Mesh/App/Core/Evaluation.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 41.3 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/Evaluation.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Mesh/App/Core/Evaluation.cpp
-
curl -L -o Evaluation.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/Evaluation.cpp
41.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; | |
| MeshOrientationVisitor::MeshOrientationVisitor() = default; | |
| bool MeshOrientationVisitor::Visit( | |
| const MeshFacet& rclFacet, | |
| const MeshFacet& rclFrom, | |
| FacetIndex ulFInd, | |
| unsigned long ulLevel | |
| ) | |
| { | |
| (void)ulFInd; | |
| (void)ulLevel; | |
| if (!rclFrom.HasSameOrientation(rclFacet)) { | |
| _nonuniformOrientation = true; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool MeshOrientationVisitor::HasNonUnifomOrientedFacets() const | |
| { | |
| return _nonuniformOrientation; | |
| } | |
| MeshOrientationCollector::MeshOrientationCollector( | |
| std::vector<FacetIndex>& aulIndices, | |
| std::vector<FacetIndex>& aulComplement | |
| ) | |
| : _aulIndices(aulIndices) | |
| , _aulComplement(aulComplement) | |
| {} | |
| bool MeshOrientationCollector::Visit( | |
| const MeshFacet& rclFacet, | |
| const MeshFacet& rclFrom, | |
| FacetIndex ulFInd, | |
| unsigned long ulLevel | |
| ) | |
| { | |
| (void)ulLevel; | |
| // different orientation of rclFacet and rclFrom | |
| if (!rclFacet.HasSameOrientation(rclFrom)) { | |
| // is not marked as false oriented | |
| if (!rclFrom.IsFlag(MeshFacet::TMP0)) { | |
| // mark this facet as false oriented | |
| rclFacet.SetFlag(MeshFacet::TMP0); | |
| _aulIndices.push_back(ulFInd); | |
| } | |
| else { | |
| _aulComplement.push_back(ulFInd); | |
| } | |
| } | |
| else { | |
| // same orientation but if the neighbour rclFrom is false oriented | |
| // then rclFrom is also false oriented | |
| if (rclFrom.IsFlag(MeshFacet::TMP0)) { | |
| // mark this facet as false oriented | |
| rclFacet.SetFlag(MeshFacet::TMP0); | |
| _aulIndices.push_back(ulFInd); | |
| } | |
| else { | |
| _aulComplement.push_back(ulFInd); | |
| } | |
| } | |
| return true; | |
| } | |
| MeshSameOrientationCollector::MeshSameOrientationCollector(std::vector<FacetIndex>& aulIndices) | |
| : _aulIndices(aulIndices) | |
| {} | |
| bool MeshSameOrientationCollector::Visit( | |
| const MeshFacet& rclFacet, | |
| const MeshFacet& rclFrom, | |
| FacetIndex ulFInd, | |
| unsigned long ulLevel | |
| ) | |
| { | |
| // different orientation of rclFacet and rclFrom | |
| (void)ulLevel; | |
| if (rclFacet.HasSameOrientation(rclFrom)) { | |
| _aulIndices.push_back(ulFInd); | |
| } | |
| return true; | |
| } | |
| // ---------------------------------------------------- | |
| MeshEvalOrientation::MeshEvalOrientation(const MeshKernel& rclM) | |
| : MeshEvaluation(rclM) | |
| {} | |
| bool MeshEvalOrientation::Evaluate() | |
| { | |
| const MeshFacetArray& rFAry = _rclMesh.GetFacets(); | |
| MeshFacetArray::_TConstIterator iBeg = rFAry.begin(); | |
| MeshFacetArray::_TConstIterator iEnd = rFAry.end(); | |
| for (auto it = iBeg; it != iEnd; ++it) { | |
| for (int i = 0; i < 3; i++) { | |
| if (it->_aulNeighbours[i] != FACET_INDEX_MAX) { | |
| const MeshFacet& rclFacet = iBeg[it->_aulNeighbours[i]]; | |
| for (int j = 0; j < 3; j++) { | |
| if (it->_aulPoints[i] == rclFacet._aulPoints[j]) { | |
| if ((it->_aulPoints[(i + 1) % 3] == rclFacet._aulPoints[(j + 1) % 3]) | |
| || (it->_aulPoints[(i + 2) % 3] == rclFacet._aulPoints[(j + 2) % 3])) { | |
| return false; // adjacent face with wrong orientation | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| unsigned long MeshEvalOrientation::HasFalsePositives(const std::vector<FacetIndex>& inds) const | |
| { | |
| // All faces with wrong orientation (i.e. adjacent faces with a normal flip and their | |
| // neighbours) build a segment and are marked as TMP0. Now we check all border faces of the | |
| // segments with their correct neighbours if there was really a normal flip. If there is no | |
| // normal flip we have a false positive. False-positives can occur if the mesh structure has | |
| // some defects which let the region-grow algorithm fail to detect the faces with wrong | |
| // orientation. | |
| const MeshFacetArray& rFAry = _rclMesh.GetFacets(); | |
| MeshFacetArray::_TConstIterator iBeg = rFAry.begin(); | |
| for (FacetIndex it : inds) { | |
| const MeshFacet& f = iBeg[it]; | |
| for (FacetIndex nbIndex : f._aulNeighbours) { | |
| if (nbIndex != FACET_INDEX_MAX) { | |
| const MeshFacet& n = iBeg[nbIndex]; | |
| if (f.IsFlag(MeshFacet::TMP0) && !n.IsFlag(MeshFacet::TMP0)) { | |
| for (int j = 0; j < 3; j++) { | |
| if (f.HasSameOrientation(n)) { | |
| // adjacent face with same orientation => false positive | |
| return nbIndex; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return FACET_INDEX_MAX; | |
| } | |
| std::vector<FacetIndex> MeshEvalOrientation::GetIndices() const | |
| { | |
| FacetIndex ulStartFacet {}, ulVisited {}; | |
| if (_rclMesh.CountFacets() == 0) { | |
| return {}; | |
| } | |
| // reset VISIT flags | |
| MeshAlgorithm cAlg(_rclMesh); | |
| cAlg.ResetFacetFlag(MeshFacet::VISIT); | |
| cAlg.ResetFacetFlag(MeshFacet::TMP0); | |
| const MeshFacetArray& rFAry = _rclMesh.GetFacets(); | |
| MeshFacetArray::_TConstIterator iTri = rFAry.begin(); | |
| MeshFacetArray::_TConstIterator iBeg = rFAry.begin(); | |
| MeshFacetArray::_TConstIterator iEnd = rFAry.end(); | |
| ulStartFacet = 0; | |
| std::vector<FacetIndex> uIndices, uComplement; | |
| MeshOrientationCollector clHarmonizer(uIndices, uComplement); | |
| while (ulStartFacet != FACET_INDEX_MAX) { | |
| unsigned long wrongFacets = uIndices.size(); | |
| uComplement.clear(); | |
| uComplement.push_back(ulStartFacet); | |
| ulVisited = _rclMesh.VisitNeighbourFacets(clHarmonizer, ulStartFacet) + 1; | |
| // In the currently visited component we have found less than 40% as correct | |
| // oriented and the rest as false oriented. So, we decide that it should be the other | |
| // way round and swap the indices of this component. | |
| if (uComplement.size() < static_cast<unsigned long>(0.4F * static_cast<float>(ulVisited))) { | |
| uIndices.erase(uIndices.begin() + wrongFacets, uIndices.end()); | |
| uIndices.insert(uIndices.end(), uComplement.begin(), uComplement.end()); | |
| } | |
| // 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. | |
| MeshIsNotFlag<MeshFacet> flag; | |
| 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; | |
| } | |
| } | |
| // in some very rare cases where we have some strange artifacts in the mesh structure | |
| // we get false-positives. If we find some we check all 'invalid' faces again | |
| cAlg.ResetFacetFlag(MeshFacet::TMP0); | |
| cAlg.SetFacetsFlag(uIndices, MeshFacet::TMP0); | |
| ulStartFacet = HasFalsePositives(uIndices); | |
| while (ulStartFacet != FACET_INDEX_MAX) { | |
| cAlg.ResetFacetsFlag(uIndices, MeshFacet::VISIT); | |
| std::vector<FacetIndex> falsePos; | |
| MeshSameOrientationCollector coll(falsePos); | |
| _rclMesh.VisitNeighbourFacets(coll, ulStartFacet); | |
| std::sort(uIndices.begin(), uIndices.end()); | |
| std::sort(falsePos.begin(), falsePos.end()); | |
| std::vector<FacetIndex> diff; | |
| std::back_insert_iterator<std::vector<FacetIndex>> biit(diff); | |
| std::set_difference(uIndices.begin(), uIndices.end(), falsePos.begin(), falsePos.end(), biit); | |
| uIndices = diff; | |
| cAlg.ResetFacetFlag(MeshFacet::TMP0); | |
| cAlg.SetFacetsFlag(uIndices, MeshFacet::TMP0); | |
| FacetIndex current = ulStartFacet; | |
| ulStartFacet = HasFalsePositives(uIndices); | |
| if (current == ulStartFacet) { | |
| break; // avoid an endless loop | |
| } | |
| } | |
| return uIndices; | |
| } | |
| MeshFixOrientation::MeshFixOrientation(MeshKernel& rclM) | |
| : MeshValidation(rclM) | |
| {} | |
| bool MeshFixOrientation::Fixup() | |
| { | |
| MeshTopoAlgorithm(_rclMesh).HarmonizeNormals(); | |
| return MeshEvalOrientation(_rclMesh).Evaluate(); | |
| } | |
| // ---------------------------------------------------- | |
| MeshEvalSolid::MeshEvalSolid(const MeshKernel& rclM) | |
| : MeshEvaluation(rclM) | |
| {} | |
| bool MeshEvalSolid::Evaluate() | |
| { | |
| std::vector<MeshGeomEdge> edges; | |
| _rclMesh.GetEdges(edges); | |
| for (const auto& it : edges) { | |
| if (it._bBorder) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // ---------------------------------------------------- | |
| namespace MeshCore | |
| { | |
| struct Edge_Index | |
| { | |
| PointIndex p0, p1; | |
| FacetIndex f; | |
| }; | |
| struct Edge_Less | |
| { | |
| bool operator()(const Edge_Index& x, const Edge_Index& y) const | |
| { | |
| if (x.p0 < y.p0) { | |
| return true; | |
| } | |
| if (x.p0 > y.p0) { | |
| return false; | |
| } | |
| if (x.p1 < y.p1) { | |
| return true; | |
| } | |
| if (x.p1 > y.p1) { | |
| return false; | |
| } | |
| return false; | |
| } | |
| }; | |
| } // namespace MeshCore | |
| bool MeshEvalTopology::Evaluate() | |
| { | |
| // Using and sorting a vector seems to be faster and more memory-efficient | |
| // than a map. | |
| const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); | |
| std::vector<Edge_Index> edges; | |
| edges.reserve(3 * rclFAry.size()); | |
| // build up an array of edges | |
| MeshFacetArray::_TConstIterator pI; | |
| Base::SequencerLauncher seq("Checking topology...", rclFAry.size()); | |
| for (pI = rclFAry.begin(); pI != rclFAry.end(); ++pI) { | |
| for (int i = 0; i < 3; i++) { | |
| Edge_Index item {}; | |
| item.p0 = std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.p1 = std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.f = pI - rclFAry.begin(); | |
| edges.push_back(item); | |
| } | |
| seq.next(); | |
| } | |
| // sort the edges | |
| std::sort(edges.begin(), edges.end(), Edge_Less()); | |
| // search for non-manifold edges | |
| PointIndex p0 = POINT_INDEX_MAX, p1 = POINT_INDEX_MAX; | |
| nonManifoldList.clear(); | |
| nonManifoldFacets.clear(); | |
| int count = 0; | |
| std::vector<FacetIndex> facets; | |
| std::vector<Edge_Index>::iterator pE; | |
| for (pE = edges.begin(); pE != edges.end(); ++pE) { | |
| if (p0 == pE->p0 && p1 == pE->p1) { | |
| count++; | |
| facets.push_back(pE->f); | |
| } | |
| else { | |
| if (count > 2) { | |
| // Edge that is shared by more than 2 facets | |
| nonManifoldList.emplace_back(p0, p1); | |
| nonManifoldFacets.push_back(facets); | |
| } | |
| p0 = pE->p0; | |
| p1 = pE->p1; | |
| facets.clear(); | |
| facets.push_back(pE->f); | |
| count = 1; | |
| } | |
| } | |
| return nonManifoldList.empty(); | |
| } | |
| // generate indexed edge list which tangents non-manifolds | |
| void MeshEvalTopology::GetFacetManifolds(std::vector<FacetIndex>& raclFacetIndList) const | |
| { | |
| raclFacetIndList.clear(); | |
| const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); | |
| MeshFacetArray::_TConstIterator pI; | |
| for (pI = rclFAry.begin(); pI != rclFAry.end(); ++pI) { | |
| for (int i = 0; i < 3; i++) { | |
| 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]); | |
| std::pair<PointIndex, PointIndex> edge = std::make_pair(ulPt0, ulPt1); | |
| if (std::ranges::find(nonManifoldList, edge) != nonManifoldList.end()) { | |
| raclFacetIndList.push_back(pI - rclFAry.begin()); | |
| } | |
| } | |
| } | |
| } | |
| unsigned long MeshEvalTopology::CountManifolds() const | |
| { | |
| return nonManifoldList.size(); | |
| } | |
| bool MeshFixTopology::Fixup() | |
| { | |
| MeshEvalTopology eval(_rclMesh); | |
| if (!eval.Evaluate()) { | |
| eval.GetFacetManifolds(deletedFaces); | |
| // remove duplicates | |
| std::sort(deletedFaces.begin(), deletedFaces.end()); | |
| deletedFaces.erase(std::unique(deletedFaces.begin(), deletedFaces.end()), deletedFaces.end()); | |
| _rclMesh.DeleteFacets(deletedFaces); | |
| } | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| deletedFaces.reserve(3 * nonManifoldList.size()); // allocate some memory | |
| for (const auto& it : nonManifoldList) { | |
| std::vector<FacetIndex> non_mf; | |
| non_mf.reserve(it.size()); | |
| for (FacetIndex jt : it) { | |
| // facet is only connected with one edge and there causes a non-manifold | |
| unsigned short numOpenEdges = rFaces[jt].CountOpenEdges(); | |
| if (numOpenEdges == 2) { | |
| non_mf.push_back(jt); | |
| } | |
| else if (rFaces[jt].IsDegenerated()) { | |
| non_mf.push_back(jt); | |
| } | |
| } | |
| // are we able to repair the non-manifold edge by not removing all facets? | |
| if (it.size() - non_mf.size() == 2) { | |
| deletedFaces.insert(deletedFaces.end(), non_mf.begin(), non_mf.end()); | |
| } | |
| else { | |
| deletedFaces.insert(deletedFaces.end(), it.begin(), it.end()); | |
| } | |
| } | |
| if (!deletedFaces.empty()) { | |
| // remove duplicates | |
| std::sort(deletedFaces.begin(), deletedFaces.end()); | |
| deletedFaces.erase(std::unique(deletedFaces.begin(), deletedFaces.end()), deletedFaces.end()); | |
| _rclMesh.DeleteFacets(deletedFaces); | |
| _rclMesh.RebuildNeighbours(); | |
| } | |
| return true; | |
| } | |
| // --------------------------------------------------------- | |
| bool MeshEvalPointManifolds::Evaluate() | |
| { | |
| this->nonManifoldPoints.clear(); | |
| this->facetsOfNonManifoldPoints.clear(); | |
| MeshCore::MeshRefPointToPoints vv_it(_rclMesh); | |
| MeshCore::MeshRefPointToFacets vf_it(_rclMesh); | |
| unsigned long ctPoints = _rclMesh.CountPoints(); | |
| for (PointIndex index = 0; index < ctPoints; index++) { | |
| // get the local neighbourhood of the point | |
| const std::set<FacetIndex>& nf = vf_it[index]; | |
| const std::set<PointIndex>& np = vv_it[index]; | |
| std::set<unsigned long>::size_type sp {}, sf {}; | |
| sp = np.size(); | |
| sf = nf.size(); | |
| // for an inner point the number of adjacent points is equal to the number of shared faces | |
| // for a boundary point the number of adjacent points is higher by one than the number of | |
| // shared faces for a non-manifold point the number of adjacent points is higher by more | |
| // than one than the number of shared faces | |
| if (sp > sf + 1) { | |
| nonManifoldPoints.push_back(index); | |
| std::vector<FacetIndex> faces; | |
| faces.insert(faces.end(), nf.begin(), nf.end()); | |
| this->facetsOfNonManifoldPoints.push_back(faces); | |
| } | |
| } | |
| return this->nonManifoldPoints.empty(); | |
| } | |
| void MeshEvalPointManifolds::GetFacetIndices(std::vector<FacetIndex>& facets) const | |
| { | |
| std::list<std::vector<FacetIndex>>::const_iterator it; | |
| for (it = facetsOfNonManifoldPoints.begin(); it != facetsOfNonManifoldPoints.end(); ++it) { | |
| facets.insert(facets.end(), it->begin(), it->end()); | |
| } | |
| if (!facets.empty()) { | |
| // remove duplicates | |
| std::sort(facets.begin(), facets.end()); | |
| facets.erase(std::unique(facets.begin(), facets.end()), facets.end()); | |
| } | |
| } | |
| // --------------------------------------------------------- | |
| bool MeshEvalSingleFacet::Evaluate() | |
| { | |
| // get all non-manifolds | |
| (void)MeshEvalTopology::Evaluate(); | |
| /* | |
| // for each (multiple) single linked facet there should | |
| // exist two valid facets sharing the same edge | |
| // so make facet 1 neighbour of facet 2 and vice versa | |
| const std::vector<MeshFacet>& rclFAry = _rclMesh.GetFacets(); | |
| std::vector<MeshFacet>::const_iterator pI; | |
| std::vector<std::list<unsigned long> > aclMf = _aclManifoldList; | |
| _aclManifoldList.clear(); | |
| std::map<std::pair<unsigned long, unsigned long>, std::list<unsigned long> > aclHits; | |
| std::map<std::pair<unsigned long, unsigned long>, std::list<unsigned long> >::iterator pEdge; | |
| // search for single links (a non-manifold edge and two open edges) | |
| // | |
| // | |
| // build edge <=> facet map | |
| for (pI = rclFAry.begin(); pI != rclFAry.end(); ++pI) | |
| { | |
| for (int i = 0; i < 3; i++) | |
| { | |
| unsigned long ulPt0 = std::min<unsigned long>(pI->_aulPoints[i], pI->_aulPoints[(i+1)%3]); | |
| unsigned long ulPt1 = std::max<unsigned long>(pI->_aulPoints[i], pI->_aulPoints[(i+1)%3]); | |
| aclHits[std::pair<unsigned long, unsigned long>(ulPt0, ulPt1)].push_front(pI - | |
| rclFAry.begin()); | |
| } | |
| } | |
| // now search for single links | |
| for (std::vector<std::list<unsigned long> >::const_iterator pMF = aclMf.begin(); pMF != | |
| aclMf.end(); ++pMF) | |
| { | |
| std::list<unsigned long> aulManifolds; | |
| for (std::list<unsigned long>::const_iterator pF = pMF->begin(); pF != pMF->end(); ++pF) | |
| { | |
| const MeshFacet& rclF = rclFAry[*pF]; | |
| unsigned long ulCtNeighbours=0; | |
| for (int i = 0; i < 3; i++) | |
| { | |
| unsigned long ulPt0 = std::min<unsigned long>(rclF._aulPoints[i], | |
| rclF._aulPoints[(i+1)%3]); unsigned long ulPt1 = std::max<unsigned long>(rclF._aulPoints[i], | |
| rclF._aulPoints[(i+1)%3]); std::pair<unsigned long, unsigned long> clEdge(ulPt0, ulPt1); | |
| // number of facets sharing this edge | |
| ulCtNeighbours += aclHits[clEdge].size(); | |
| } | |
| // single linked found | |
| if (ulCtNeighbours == pMF->size() + 2) | |
| aulManifolds.push_front(*pF); | |
| } | |
| if ( aulManifolds.size() > 0 ) | |
| _aclManifoldList.push_back(aulManifolds); | |
| } | |
| */ | |
| return (nonManifoldList.empty()); | |
| } | |
| bool MeshFixSingleFacet::Fixup() | |
| { | |
| std::vector<FacetIndex> aulInvalids; | |
| for (const auto& it : _raclManifoldList) { | |
| for (FacetIndex it2 : it) { | |
| aulInvalids.push_back(it2); | |
| } | |
| } | |
| _rclMesh.DeleteFacets(aulInvalids); | |
| return true; | |
| } | |
| // ---------------------------------------------------------------- | |
| bool MeshEvalSelfIntersection::Evaluate() | |
| { | |
| // Contains bounding boxes for every facet | |
| std::vector<Base::BoundBox3f> boxes; | |
| // Splits the mesh using grid for speeding up the calculation | |
| MeshFacetGrid cMeshFacetGrid(_rclMesh); | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| MeshGridIterator clGridIter(cMeshFacetGrid); | |
| unsigned long ulGridX {}, ulGridY {}, ulGridZ {}; | |
| cMeshFacetGrid.GetCtGrids(ulGridX, ulGridY, ulGridZ); | |
| MeshFacetIterator cMFI(_rclMesh); | |
| for (cMFI.Begin(); cMFI.More(); cMFI.Next()) { | |
| boxes.push_back((*cMFI).GetBoundBox()); | |
| } | |
| // Calculates the intersections | |
| Base::SequencerLauncher seq("Checking for self-intersections...", ulGridX * ulGridY * ulGridZ); | |
| for (clGridIter.Init(); clGridIter.More(); clGridIter.Next()) { | |
| // Get the facet indices, belonging to the current grid unit | |
| std::vector<FacetIndex> aulGridElements; | |
| clGridIter.GetElements(aulGridElements); | |
| seq.next(); | |
| if (aulGridElements.empty()) { | |
| continue; | |
| } | |
| MeshGeomFacet facet1, facet2; | |
| Base::Vector3f pt1, pt2; | |
| for (auto it = aulGridElements.begin(); it != aulGridElements.end(); ++it) { | |
| const Base::BoundBox3f& box1 = boxes[*it]; | |
| cMFI.Set(*it); | |
| facet1 = *cMFI; | |
| const MeshFacet& rface1 = rFaces[*it]; | |
| for (auto jt = it; jt != aulGridElements.end(); ++jt) { | |
| if (jt == it) { // the identical facet | |
| continue; | |
| } | |
| // If the facets share a common vertex we do not check for self-intersections | |
| // because they could but usually do not intersect each other and the algorithm | |
| // below would detect false-positives, otherwise | |
| const MeshFacet& rface2 = rFaces[*jt]; | |
| if (rface1._aulPoints[0] == rface2._aulPoints[0] | |
| || rface1._aulPoints[0] == rface2._aulPoints[1] | |
| || rface1._aulPoints[0] == rface2._aulPoints[2]) { | |
| continue; // ignore facets sharing a common vertex | |
| } | |
| if (rface1._aulPoints[1] == rface2._aulPoints[0] | |
| || rface1._aulPoints[1] == rface2._aulPoints[1] | |
| || rface1._aulPoints[1] == rface2._aulPoints[2]) { | |
| continue; // ignore facets sharing a common vertex | |
| } | |
| if (rface1._aulPoints[2] == rface2._aulPoints[0] | |
| || rface1._aulPoints[2] == rface2._aulPoints[1] | |
| || rface1._aulPoints[2] == rface2._aulPoints[2]) { | |
| continue; // ignore facets sharing a common vertex | |
| } | |
| const Base::BoundBox3f& box2 = boxes[*jt]; | |
| if (box1 && box2) { | |
| cMFI.Set(*jt); | |
| facet2 = *cMFI; | |
| int ret = facet1.IntersectWithFacet(facet2, pt1, pt2); | |
| if (ret == 2) { | |
| // abort after the first detected self-intersection | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| void MeshEvalSelfIntersection::GetIntersections( | |
| const std::vector<std::pair<FacetIndex, FacetIndex>>& indices, | |
| std::vector<std::pair<Base::Vector3f, Base::Vector3f>>& intersection | |
| ) const | |
| { | |
| intersection.reserve(indices.size()); | |
| MeshFacetIterator cMF1(_rclMesh); | |
| MeshFacetIterator cMF2(_rclMesh); | |
| Base::Vector3f pt1, pt2; | |
| std::vector<std::pair<FacetIndex, FacetIndex>>::const_iterator it; | |
| for (it = indices.begin(); it != indices.end(); ++it) { | |
| cMF1.Set(it->first); | |
| cMF2.Set(it->second); | |
| Base::BoundBox3f box1 = cMF1->GetBoundBox(); | |
| Base::BoundBox3f box2 = cMF2->GetBoundBox(); | |
| if (box1 && box2) { | |
| int ret = cMF1->IntersectWithFacet(*cMF2, pt1, pt2); | |
| if (ret == 2) { | |
| intersection.emplace_back(pt1, pt2); | |
| } | |
| } | |
| } | |
| } | |
| void MeshEvalSelfIntersection::GetIntersections( | |
| std::vector<std::pair<FacetIndex, FacetIndex>>& intersection | |
| ) const | |
| { | |
| // Contains bounding boxes for every facet | |
| std::vector<Base::BoundBox3f> boxes; | |
| // intersection.clear(); | |
| // Splits the mesh using grid for speeding up the calculation | |
| MeshFacetGrid cMeshFacetGrid(_rclMesh); | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| MeshGridIterator clGridIter(cMeshFacetGrid); | |
| unsigned long ulGridX {}, ulGridY {}, ulGridZ {}; | |
| cMeshFacetGrid.GetCtGrids(ulGridX, ulGridY, ulGridZ); | |
| MeshFacetIterator cMFI(_rclMesh); | |
| for (cMFI.Begin(); cMFI.More(); cMFI.Next()) { | |
| boxes.push_back((*cMFI).GetBoundBox()); | |
| } | |
| // Calculates the intersections | |
| Base::SequencerLauncher seq("Checking for self-intersections...", ulGridX * ulGridY * ulGridZ); | |
| for (clGridIter.Init(); clGridIter.More(); clGridIter.Next()) { | |
| // Get the facet indices, belonging to the current grid unit | |
| std::vector<FacetIndex> aulGridElements; | |
| clGridIter.GetElements(aulGridElements); | |
| seq.next(true); | |
| if (aulGridElements.empty()) { | |
| continue; | |
| } | |
| MeshGeomFacet facet1, facet2; | |
| Base::Vector3f pt1, pt2; | |
| for (auto it = aulGridElements.begin(); it != aulGridElements.end(); ++it) { | |
| const Base::BoundBox3f& box1 = boxes[*it]; | |
| cMFI.Set(*it); | |
| facet1 = *cMFI; | |
| const MeshFacet& rface1 = rFaces[*it]; | |
| for (auto jt = it; jt != aulGridElements.end(); ++jt) { | |
| if (jt == it) { // the identical facet | |
| continue; | |
| } | |
| // If the facets share a common vertex we do not check for self-intersections | |
| // because they could but usually do not intersect each other and the algorithm | |
| // below would detect false-positives, otherwise | |
| const MeshFacet& rface2 = rFaces[*jt]; | |
| if (rface1._aulPoints[0] == rface2._aulPoints[0] | |
| || rface1._aulPoints[0] == rface2._aulPoints[1] | |
| || rface1._aulPoints[0] == rface2._aulPoints[2]) { | |
| continue; // ignore facets sharing a common vertex | |
| } | |
| if (rface1._aulPoints[1] == rface2._aulPoints[0] | |
| || rface1._aulPoints[1] == rface2._aulPoints[1] | |
| || rface1._aulPoints[1] == rface2._aulPoints[2]) { | |
| continue; // ignore facets sharing a common vertex | |
| } | |
| if (rface1._aulPoints[2] == rface2._aulPoints[0] | |
| || rface1._aulPoints[2] == rface2._aulPoints[1] | |
| || rface1._aulPoints[2] == rface2._aulPoints[2]) { | |
| continue; // ignore facets sharing a common vertex | |
| } | |
| const Base::BoundBox3f& box2 = boxes[*jt]; | |
| if (box1 && box2) { | |
| cMFI.Set(*jt); | |
| facet2 = *cMFI; | |
| int ret = facet1.IntersectWithFacet(facet2, pt1, pt2); | |
| if (ret == 2) { | |
| intersection.emplace_back(*it, *jt); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| std::vector<FacetIndex> MeshFixSelfIntersection::GetFacets() const | |
| { | |
| std::vector<FacetIndex> indices; | |
| const MeshFacetArray& rFaces = _rclMesh.GetFacets(); | |
| for (const auto& it : selfIntersectons) { | |
| unsigned short numOpenEdges1 = rFaces[it.first].CountOpenEdges(); | |
| unsigned short numOpenEdges2 = rFaces[it.second].CountOpenEdges(); | |
| // often we have only single or border facets that intersect other facets | |
| // in this case remove only these facets and keep the other one | |
| if (numOpenEdges1 == 0 && numOpenEdges2 > 0) { | |
| indices.push_back(it.second); | |
| } | |
| else if (numOpenEdges1 > 0 && numOpenEdges2 == 0) { | |
| indices.push_back(it.first); | |
| } | |
| else { | |
| indices.push_back(it.first); | |
| indices.push_back(it.second); | |
| } | |
| } | |
| // remove duplicates | |
| std::sort(indices.begin(), indices.end()); | |
| indices.erase(std::unique(indices.begin(), indices.end()), indices.end()); | |
| return indices; | |
| } | |
| bool MeshFixSelfIntersection::Fixup() | |
| { | |
| _rclMesh.DeleteFacets(GetFacets()); | |
| return true; | |
| } | |
| // ---------------------------------------------------------------- | |
| bool MeshEvalNeighbourhood::Evaluate() | |
| { | |
| // Note: If more than two facets are attached to the edge then we have a | |
| // non-manifold edge here. | |
| // This means that the neighbourhood cannot be valid, for sure. But we just | |
| // want to check whether the neighbourhood is valid for topologic correctly | |
| // edges and thus we ignore this case. | |
| // Non-manifolds are an own category of errors and are handled by the class | |
| // MeshEvalTopology. | |
| // | |
| // Using and sorting a vector seems to be faster and more memory-efficient | |
| // than a map. | |
| const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); | |
| std::vector<Edge_Index> edges; | |
| edges.reserve(3 * rclFAry.size()); | |
| // build up an array of edges | |
| MeshFacetArray::_TConstIterator pI; | |
| Base::SequencerLauncher seq("Checking indices...", rclFAry.size()); | |
| for (pI = rclFAry.begin(); pI != rclFAry.end(); ++pI) { | |
| for (int i = 0; i < 3; i++) { | |
| Edge_Index item {}; | |
| item.p0 = std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.p1 = std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.f = pI - rclFAry.begin(); | |
| edges.push_back(item); | |
| } | |
| seq.next(); | |
| } | |
| // sort the edges | |
| std::sort(edges.begin(), edges.end(), Edge_Less()); | |
| PointIndex p0 = POINT_INDEX_MAX, p1 = POINT_INDEX_MAX; | |
| PointIndex f0 = FACET_INDEX_MAX, f1 = FACET_INDEX_MAX; | |
| int count = 0; | |
| std::vector<Edge_Index>::iterator pE; | |
| for (pE = edges.begin(); pE != edges.end(); ++pE) { | |
| if (p0 == pE->p0 && p1 == pE->p1) { | |
| f1 = pE->f; | |
| count++; | |
| } | |
| else { | |
| // we handle only the cases for 1 and 2, for all higher | |
| // values we have a non-manifold that is ignored here | |
| if (count == 2) { | |
| const MeshFacet& rFace0 = rclFAry[f0]; | |
| const MeshFacet& rFace1 = rclFAry[f1]; | |
| unsigned short side0 = rFace0.Side(p0, p1); | |
| unsigned short side1 = rFace1.Side(p0, p1); | |
| // Check whether rFace0 and rFace1 reference each other as | |
| // neighbours | |
| if (rFace0._aulNeighbours[side0] != f1 || rFace1._aulNeighbours[side1] != f0) { | |
| return false; | |
| } | |
| } | |
| else if (count == 1) { | |
| const MeshFacet& rFace = rclFAry[f0]; | |
| unsigned short side = rFace.Side(p0, p1); | |
| // should be "open edge" but isn't marked as such | |
| if (rFace._aulNeighbours[side] != FACET_INDEX_MAX) { | |
| return false; | |
| } | |
| } | |
| p0 = pE->p0; | |
| p1 = pE->p1; | |
| f0 = pE->f; | |
| count = 1; | |
| } | |
| } | |
| return true; | |
| } | |
| std::vector<FacetIndex> MeshEvalNeighbourhood::GetIndices() const | |
| { | |
| std::vector<FacetIndex> inds; | |
| const MeshFacetArray& rclFAry = _rclMesh.GetFacets(); | |
| std::vector<Edge_Index> edges; | |
| edges.reserve(3 * rclFAry.size()); | |
| // build up an array of edges | |
| MeshFacetArray::_TConstIterator pI; | |
| Base::SequencerLauncher seq("Checking indices...", rclFAry.size()); | |
| for (pI = rclFAry.begin(); pI != rclFAry.end(); ++pI) { | |
| for (int i = 0; i < 3; i++) { | |
| Edge_Index item {}; | |
| item.p0 = std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.p1 = std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.f = pI - rclFAry.begin(); | |
| edges.push_back(item); | |
| } | |
| seq.next(); | |
| } | |
| // sort the edges | |
| std::sort(edges.begin(), edges.end(), Edge_Less()); | |
| PointIndex p0 = POINT_INDEX_MAX, p1 = POINT_INDEX_MAX; | |
| PointIndex f0 = FACET_INDEX_MAX, f1 = FACET_INDEX_MAX; | |
| int count = 0; | |
| std::vector<Edge_Index>::iterator pE; | |
| for (pE = edges.begin(); pE != edges.end(); ++pE) { | |
| if (p0 == pE->p0 && p1 == pE->p1) { | |
| f1 = pE->f; | |
| count++; | |
| } | |
| else { | |
| // we handle only the cases for 1 and 2, for all higher | |
| // values we have a non-manifold that is ignored here | |
| if (count == 2) { | |
| const MeshFacet& rFace0 = rclFAry[f0]; | |
| const MeshFacet& rFace1 = rclFAry[f1]; | |
| unsigned short side0 = rFace0.Side(p0, p1); | |
| unsigned short side1 = rFace1.Side(p0, p1); | |
| // Check whether rFace0 and rFace1 reference each other as | |
| // neighbours | |
| if (rFace0._aulNeighbours[side0] != f1 || rFace1._aulNeighbours[side1] != f0) { | |
| inds.push_back(f0); | |
| inds.push_back(f1); | |
| } | |
| } | |
| else if (count == 1) { | |
| const MeshFacet& rFace = rclFAry[f0]; | |
| unsigned short side = rFace.Side(p0, p1); | |
| // should be "open edge" but isn't marked as such | |
| if (rFace._aulNeighbours[side] != FACET_INDEX_MAX) { | |
| inds.push_back(f0); | |
| } | |
| } | |
| p0 = pE->p0; | |
| p1 = pE->p1; | |
| f0 = pE->f; | |
| count = 1; | |
| } | |
| } | |
| // remove duplicates | |
| std::sort(inds.begin(), inds.end()); | |
| inds.erase(std::unique(inds.begin(), inds.end()), inds.end()); | |
| return inds; | |
| } | |
| bool MeshFixNeighbourhood::Fixup() | |
| { | |
| _rclMesh.RebuildNeighbours(); | |
| return true; | |
| } | |
| void MeshKernel::RebuildNeighbours(FacetIndex index) | |
| { | |
| std::vector<Edge_Index> edges; | |
| edges.reserve(3 * (this->_aclFacetArray.size() - index)); | |
| // build up an array of edges | |
| auto pB = this->_aclFacetArray.begin(); | |
| for (auto pI = pB + index; pI != this->_aclFacetArray.end(); ++pI) { | |
| for (int i = 0; i < 3; i++) { | |
| Edge_Index item {}; | |
| item.p0 = std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.p1 = std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]); | |
| item.f = pI - pB; | |
| edges.push_back(item); | |
| } | |
| } | |
| // sort the edges | |
| // std::sort(edges.begin(), edges.end(), Edge_Less()); | |
| int threads = int(std::thread::hardware_concurrency()); | |
| MeshCore::parallel_sort(edges.begin(), edges.end(), Edge_Less(), threads); | |
| PointIndex p0 = POINT_INDEX_MAX, p1 = POINT_INDEX_MAX; | |
| PointIndex f0 = FACET_INDEX_MAX, f1 = FACET_INDEX_MAX; | |
| int count = 0; | |
| std::vector<Edge_Index>::iterator pE; | |
| for (pE = edges.begin(); pE != edges.end(); ++pE) { | |
| if (p0 == pE->p0 && p1 == pE->p1) { | |
| f1 = pE->f; | |
| count++; | |
| } | |
| else { | |
| // we handle only the cases for 1 and 2, for all higher | |
| // values we have a non-manifold that is ignored here | |
| if (count == 2) { | |
| MeshFacet& rFace0 = this->_aclFacetArray[f0]; | |
| MeshFacet& rFace1 = this->_aclFacetArray[f1]; | |
| unsigned short side0 = rFace0.Side(p0, p1); | |
| unsigned short side1 = rFace1.Side(p0, p1); | |
| rFace0._aulNeighbours[side0] = f1; | |
| rFace1._aulNeighbours[side1] = f0; | |
| } | |
| else if (count == 1) { | |
| MeshFacet& rFace = this->_aclFacetArray[f0]; | |
| unsigned short side = rFace.Side(p0, p1); | |
| rFace._aulNeighbours[side] = FACET_INDEX_MAX; | |
| } | |
| p0 = pE->p0; | |
| p1 = pE->p1; | |
| f0 = pE->f; | |
| count = 1; | |
| } | |
| } | |
| // we handle only the cases for 1 and 2, for all higher | |
| // values we have a non-manifold that is ignored here | |
| if (count == 2) { | |
| MeshFacet& rFace0 = this->_aclFacetArray[f0]; | |
| MeshFacet& rFace1 = this->_aclFacetArray[f1]; | |
| unsigned short side0 = rFace0.Side(p0, p1); | |
| unsigned short side1 = rFace1.Side(p0, p1); | |
| rFace0._aulNeighbours[side0] = f1; | |
| rFace1._aulNeighbours[side1] = f0; | |
| } | |
| else if (count == 1) { | |
| MeshFacet& rFace = this->_aclFacetArray[f0]; | |
| unsigned short side = rFace.Side(p0, p1); | |
| rFace._aulNeighbours[side] = FACET_INDEX_MAX; | |
| } | |
| } | |
| void MeshKernel::RebuildNeighbours() | |
| { | |
| // complete rebuild | |
| RebuildNeighbours(0); | |
| } | |
| // ---------------------------------------------------------------- | |
| MeshEigensystem::MeshEigensystem(const MeshKernel& rclB) | |
| : MeshEvaluation(rclB) | |
| , _cU(1.0F, 0.0F, 0.0F) | |
| , _cV(0.0F, 1.0F, 0.0F) | |
| , _cW(0.0F, 0.0F, 1.0F) | |
| { | |
| // use the values of world coordinates as default | |
| Base::BoundBox3f box = _rclMesh.GetBoundBox(); | |
| _fU = box.LengthX(); | |
| _fV = box.LengthY(); | |
| _fW = box.LengthZ(); | |
| } | |
| Base::Matrix4D MeshEigensystem::Transform() const | |
| { | |
| // x,y,c ... vectors | |
| // R,Q ... matrices (R is orthonormal so its transposed(=inverse) is equal to Q) | |
| // | |
| // from local (x) to world (y,c) coordinates we have the equation | |
| // y = R * x + c | |
| // <==> | |
| // x = Q * y - Q * c | |
| Base::Matrix4D clTMat; | |
| // rotation part | |
| clTMat[0][0] = double(_cU.x); | |
| clTMat[0][1] = double(_cU.y); | |
| clTMat[0][2] = double(_cU.z); | |
| clTMat[0][3] = 0.0; | |
| clTMat[1][0] = double(_cV.x); | |
| clTMat[1][1] = double(_cV.y); | |
| clTMat[1][2] = double(_cV.z); | |
| clTMat[1][3] = 0.0; | |
| clTMat[2][0] = double(_cW.x); | |
| clTMat[2][1] = double(_cW.y); | |
| clTMat[2][2] = double(_cW.z); | |
| clTMat[2][3] = 0.0; | |
| clTMat[3][0] = 0.0; | |
| clTMat[3][1] = 0.0; | |
| clTMat[3][2] = 0.0; | |
| clTMat[3][3] = 1.0; | |
| Base::Vector3f c(_cC); | |
| c = clTMat * c; | |
| // translation part | |
| clTMat[0][3] = double(-c.x); | |
| clTMat[1][3] = double(-c.y); | |
| clTMat[2][3] = double(-c.z); | |
| return clTMat; | |
| } | |
| bool MeshEigensystem::Evaluate() | |
| { | |
| CalculateLocalSystem(); | |
| float xmin = 0.0F, xmax = 0.0F, ymin = 0.0F, ymax = 0.0F, zmin = 0.0F, zmax = 0.0F; | |
| Base::Vector3f clVect, clProj; | |
| float fH {}; | |
| const MeshPointArray& aclPoints = _rclMesh.GetPoints(); | |
| for (const auto& it : aclPoints) { | |
| // u-direction | |
| clVect = it - _cC; | |
| clProj.ProjectToLine(clVect, _cU); | |
| clVect = clVect + clProj; | |
| fH = clVect.Length(); | |
| // point vectors in the same direction ? | |
| if ((clVect * _cU) < 0.0F) { | |
| fH = -fH; | |
| } | |
| xmax = std::max<float>(xmax, fH); | |
| xmin = std::min<float>(xmin, fH); | |
| // v-direction | |
| clVect = it - _cC; | |
| clProj.ProjectToLine(clVect, _cV); | |
| clVect = clVect + clProj; | |
| fH = clVect.Length(); | |
| // point vectors in the same direction ? | |
| if ((clVect * _cV) < 0.0F) { | |
| fH = -fH; | |
| } | |
| ymax = std::max<float>(ymax, fH); | |
| ymin = std::min<float>(ymin, fH); | |
| // w-direction | |
| clVect = it - _cC; | |
| clProj.ProjectToLine(clVect, _cW); | |
| clVect = clVect + clProj; | |
| fH = clVect.Length(); | |
| // point vectors in the same direction ? | |
| if ((clVect * _cW) < 0.0F) { | |
| fH = -fH; | |
| } | |
| zmax = std::max<float>(zmax, fH); | |
| zmin = std::min<float>(zmin, fH); | |
| } | |
| _fU = xmax - xmin; | |
| _fV = ymax - ymin; | |
| _fW = zmax - zmin; | |
| return false; // to call Fixup() if needed | |
| } | |
| Base::Vector3f MeshEigensystem::GetBoundings() const | |
| { | |
| return Base::Vector3f(_fU, _fV, _fW); | |
| } | |
| void MeshEigensystem::CalculateLocalSystem() | |
| { | |
| // at least one facet is needed | |
| if (_rclMesh.CountFacets() < 1) { | |
| return; // cannot continue calculation | |
| } | |
| const MeshPointArray& aclPoints = _rclMesh.GetPoints(); | |
| MeshPointArray::_TConstIterator it; | |
| PlaneFit planeFit; | |
| for (it = aclPoints.begin(); it != aclPoints.end(); ++it) { | |
| planeFit.AddPoint(*it); | |
| } | |
| planeFit.Fit(); | |
| _cC = planeFit.GetBase(); | |
| _cU = planeFit.GetDirU(); | |
| _cV = planeFit.GetDirV(); | |
| _cW = planeFit.GetNormal(); | |
| // set the sign for the vectors | |
| float fSumU {0.0F}, fSumV {0.0F}, fSumW {0.0F}; | |
| for (it = aclPoints.begin(); it != aclPoints.end(); ++it) { | |
| float fU = _cU * (*it - _cC); | |
| float fV = _cV * (*it - _cC); | |
| float fW = _cW * (*it - _cC); | |
| fSumU += (fU > 0 ? fU * fU : -fU * fU); | |
| fSumV += (fV > 0 ? fV * fV : -fV * fV); | |
| fSumW += (fW > 0 ? fW * fW : -fW * fW); | |
| } | |
| // avoid ambiguities concerning directions | |
| if (fSumU < 0.0F) { | |
| _cU *= -1.0F; | |
| } | |
| if (fSumV < 0.0F) { | |
| _cV *= -1.0F; | |
| } | |
| if (fSumW < 0.0F) { | |
| _cW *= -1.0F; | |
| } | |
| if ((_cU % _cV) * _cW < 0.0F) { | |
| _cW = -_cW; // make a right-handed system | |
| } | |
| } | |