Download src/Mod/Mesh/App/Core/MeshKernel.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 42 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/MeshKernel.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Mesh/App/Core/MeshKernel.cpp
-
curl -L -o MeshKernel.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/MeshKernel.cpp
42 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; | |
| MeshKernel::MeshKernel() | |
| { | |
| _clBoundBox.SetVoid(); | |
| } | |
| MeshKernel::MeshKernel(const MeshKernel& rclMesh) | |
| { | |
| *this = rclMesh; | |
| } | |
| MeshKernel::MeshKernel(MeshKernel&& rclMesh) | |
| { | |
| *this = rclMesh; | |
| } | |
| MeshKernel& MeshKernel::operator=(const MeshKernel& rclMesh) | |
| { | |
| if (this != &rclMesh) { // must be a different instance | |
| this->_aclPointArray = rclMesh._aclPointArray; | |
| this->_aclFacetArray = rclMesh._aclFacetArray; | |
| this->_clBoundBox = rclMesh._clBoundBox; | |
| this->_bValid = rclMesh._bValid; | |
| } | |
| return *this; | |
| } | |
| MeshKernel& MeshKernel::operator=(MeshKernel&& rclMesh) | |
| { | |
| if (this != &rclMesh) { // must be a different instance | |
| this->_aclPointArray = std::move(rclMesh._aclPointArray); | |
| this->_aclFacetArray = std::move(rclMesh._aclFacetArray); | |
| this->_clBoundBox = rclMesh._clBoundBox; | |
| this->_bValid = rclMesh._bValid; | |
| } | |
| return *this; | |
| } | |
| MeshKernel& MeshKernel::operator=(const std::vector<MeshGeomFacet>& rclFAry) | |
| { | |
| MeshBuilder builder(*this); | |
| builder.Initialize(rclFAry.size()); | |
| for (const auto& it : rclFAry) { | |
| builder.AddFacet(it); | |
| } | |
| builder.Finish(); | |
| return *this; | |
| } | |
| void MeshKernel::Assign(const MeshPointArray& rPoints, const MeshFacetArray& rFacets, bool checkNeighbourHood) | |
| { | |
| _aclPointArray = rPoints; | |
| _aclFacetArray = rFacets; | |
| RecalcBoundBox(); | |
| if (checkNeighbourHood) { | |
| RebuildNeighbours(); | |
| } | |
| } | |
| void MeshKernel::Adopt(MeshPointArray& rPoints, MeshFacetArray& rFacets, bool checkNeighbourHood) | |
| { | |
| _aclPointArray.swap(rPoints); | |
| _aclFacetArray.swap(rFacets); | |
| RecalcBoundBox(); | |
| if (checkNeighbourHood) { | |
| RebuildNeighbours(); | |
| } | |
| } | |
| void MeshKernel::Swap(MeshKernel& mesh) | |
| { | |
| this->_aclPointArray.swap(mesh._aclPointArray); | |
| this->_aclFacetArray.swap(mesh._aclFacetArray); | |
| this->_clBoundBox = mesh._clBoundBox; | |
| } | |
| MeshKernel& MeshKernel::operator+=(const MeshGeomFacet& rclSFacet) | |
| { | |
| this->AddFacet(rclSFacet); | |
| return *this; | |
| } | |
| void MeshKernel::AddFacet(const MeshGeomFacet& rclSFacet) | |
| { | |
| MeshFacet clFacet; | |
| // set corner points | |
| for (int i = 0; i < 3; i++) { | |
| _clBoundBox.Add(rclSFacet._aclPoints[i]); | |
| clFacet._aulPoints[i] = _aclPointArray.GetOrAddIndex(rclSFacet._aclPoints[i]); | |
| } | |
| // adjust orientation to normal | |
| AdjustNormal(clFacet, rclSFacet.GetNormal()); | |
| FacetIndex ulCt = _aclFacetArray.size(); | |
| // set neighbourhood | |
| PointIndex ulP0 = clFacet._aulPoints[0]; | |
| PointIndex ulP1 = clFacet._aulPoints[1]; | |
| PointIndex ulP2 = clFacet._aulPoints[2]; | |
| FacetIndex ulCC = 0; | |
| for (auto pF = _aclFacetArray.begin(); pF != _aclFacetArray.end(); ++pF, ulCC++) { | |
| for (int i = 0; i < 3; i++) { | |
| PointIndex ulP = pF->_aulPoints[i]; | |
| PointIndex ulQ = pF->_aulPoints[(i + 1) % 3]; | |
| if (ulQ == ulP0 && ulP == ulP1) { | |
| clFacet._aulNeighbours[0] = ulCC; | |
| pF->_aulNeighbours[i] = ulCt; | |
| } | |
| else if (ulQ == ulP1 && ulP == ulP2) { | |
| clFacet._aulNeighbours[1] = ulCC; | |
| pF->_aulNeighbours[i] = ulCt; | |
| } | |
| else if (ulQ == ulP2 && ulP == ulP0) { | |
| clFacet._aulNeighbours[2] = ulCC; | |
| pF->_aulNeighbours[i] = ulCt; | |
| } | |
| } | |
| } | |
| // insert facet into array | |
| _aclFacetArray.push_back(clFacet); | |
| } | |
| MeshKernel& MeshKernel::operator+=(const std::vector<MeshGeomFacet>& rclFAry) | |
| { | |
| this->AddFacets(rclFAry); | |
| return *this; | |
| } | |
| void MeshKernel::AddFacets(const std::vector<MeshGeomFacet>& rclFAry) | |
| { | |
| // Create a temp. kernel to get the topology of the passed triangles | |
| // and merge them with this kernel. This keeps properties and flags | |
| // of this mesh. | |
| MeshKernel tmp; | |
| tmp = rclFAry; | |
| Merge(tmp); | |
| } | |
| unsigned long MeshKernel::AddFacets(const std::vector<MeshFacet>& rclFAry, bool checkManifolds) | |
| { | |
| // Build map of edges of the referencing facets we want to append | |
| [[maybe_unused]] unsigned long countPoints = CountPoints(); | |
| // if the manifold check shouldn't be done then just add all faces | |
| if (!checkManifolds) { | |
| FacetIndex countFacets = CountFacets(); | |
| FacetIndex countValid = rclFAry.size(); | |
| _aclFacetArray.reserve(countFacets + countValid); | |
| // just add all faces now | |
| for (const auto& pF : rclFAry) { | |
| _aclFacetArray.push_back(pF); | |
| } | |
| RebuildNeighbours(countFacets); | |
| return _aclFacetArray.size(); | |
| } | |
| this->_aclPointArray.ResetInvalid(); | |
| FacetIndex k = CountFacets(); | |
| std::map<std::pair<PointIndex, PointIndex>, std::list<FacetIndex>> edgeMap; | |
| for (auto pF = rclFAry.begin(); pF != rclFAry.end(); ++pF, k++) { | |
| // reset INVALID flag for all candidates | |
| pF->ResetFlag(MeshFacet::INVALID); | |
| for (int i = 0; i < 3; i++) { | |
| assert(pF->_aulPoints[i] < countPoints); | |
| this->_aclPointArray[pF->_aulPoints[i]].SetFlag(MeshPoint::INVALID); | |
| PointIndex ulT0 = pF->_aulPoints[i]; | |
| PointIndex ulT1 = pF->_aulPoints[(i + 1) % 3]; | |
| PointIndex ulP0 = std::min<PointIndex>(ulT0, ulT1); | |
| PointIndex ulP1 = std::max<PointIndex>(ulT0, ulT1); | |
| edgeMap[std::make_pair(ulP0, ulP1)].push_front(k); | |
| } | |
| } | |
| // Check for the above edges in the current facet array | |
| k = 0; | |
| for (auto pF = _aclFacetArray.begin(); pF != _aclFacetArray.end(); ++pF, k++) { | |
| // if none of the points references one of the edges ignore the facet | |
| if (!this->_aclPointArray[pF->_aulPoints[0]].IsFlag(MeshPoint::INVALID) | |
| && !this->_aclPointArray[pF->_aulPoints[1]].IsFlag(MeshPoint::INVALID) | |
| && !this->_aclPointArray[pF->_aulPoints[2]].IsFlag(MeshPoint::INVALID)) { | |
| continue; | |
| } | |
| for (int i = 0; i < 3; i++) { | |
| PointIndex ulT0 = pF->_aulPoints[i]; | |
| PointIndex ulT1 = pF->_aulPoints[(i + 1) % 3]; | |
| PointIndex ulP0 = std::min<PointIndex>(ulT0, ulT1); | |
| PointIndex ulP1 = std::max<PointIndex>(ulT0, ulT1); | |
| std::pair<PointIndex, PointIndex> edge = std::make_pair(ulP0, ulP1); | |
| auto pI = edgeMap.find(edge); | |
| // Does the current facet share the same edge? | |
| if (pI != edgeMap.end()) { | |
| pI->second.push_front(k); | |
| } | |
| } | |
| } | |
| this->_aclPointArray.ResetInvalid(); | |
| // Now let's see for which edges we might get manifolds, if so we don't add the corresponding | |
| // candidates | |
| FacetIndex countFacets = CountFacets(); | |
| std::map<std::pair<PointIndex, PointIndex>, std::list<FacetIndex>>::iterator pE; | |
| for (pE = edgeMap.begin(); pE != edgeMap.end(); ++pE) { | |
| if (pE->second.size() > 2) { | |
| for (FacetIndex it : pE->second) { | |
| if (it >= countFacets) { | |
| // this is a candidate | |
| FacetIndex index = it - countFacets; | |
| rclFAry[index].SetFlag(MeshFacet::INVALID); | |
| } | |
| } | |
| } | |
| } | |
| // Do not insert directly to the data structure because we should get the correct size of new | |
| // facets, otherwise std::vector reallocates too much memory which can't be freed so easily | |
| MeshIsNotFlag<MeshFacet> flag; | |
| FacetIndex countValid = std::count_if(rclFAry.begin(), rclFAry.end(), [flag](const MeshFacet& f) { | |
| return flag(f, MeshFacet::INVALID); | |
| }); | |
| _aclFacetArray.reserve(_aclFacetArray.size() + countValid); | |
| // now start inserting the facets to the data structure and set the correct neighbourhood as | |
| // well | |
| FacetIndex startIndex = CountFacets(); | |
| for (const auto& pF : rclFAry) { | |
| if (!pF.IsFlag(MeshFacet::INVALID)) { | |
| _aclFacetArray.push_back(pF); | |
| pF.SetProperty(startIndex++); | |
| } | |
| } | |
| // resolve neighbours | |
| for (pE = edgeMap.begin(); pE != edgeMap.end(); ++pE) { | |
| PointIndex ulP0 = pE->first.first; | |
| PointIndex ulP1 = pE->first.second; | |
| if (pE->second.size() == 1) // border facet | |
| { | |
| FacetIndex ulF0 = pE->second.front(); | |
| if (ulF0 >= countFacets) { | |
| ulF0 -= countFacets; | |
| std::vector<MeshFacet>::const_iterator pF = rclFAry.begin() + ulF0; | |
| if (!pF->IsFlag(MeshFacet::INVALID)) { | |
| ulF0 = pF->_ulProp; | |
| } | |
| else { | |
| ulF0 = FACET_INDEX_MAX; | |
| } | |
| } | |
| if (ulF0 != FACET_INDEX_MAX) { | |
| unsigned short usSide = _aclFacetArray[ulF0].Side(ulP0, ulP1); | |
| assert(usSide != std::numeric_limits<unsigned short>::max()); | |
| _aclFacetArray[ulF0]._aulNeighbours[usSide] = FACET_INDEX_MAX; | |
| } | |
| } | |
| else if (pE->second.size() == 2) // normal facet with neighbour | |
| { | |
| // we must check if both facets are part of the mesh now | |
| FacetIndex ulF0 = pE->second.front(); | |
| if (ulF0 >= countFacets) { | |
| ulF0 -= countFacets; | |
| std::vector<MeshFacet>::const_iterator pF = rclFAry.begin() + ulF0; | |
| if (!pF->IsFlag(MeshFacet::INVALID)) { | |
| ulF0 = pF->_ulProp; | |
| } | |
| else { | |
| ulF0 = FACET_INDEX_MAX; | |
| } | |
| } | |
| FacetIndex ulF1 = pE->second.back(); | |
| if (ulF1 >= countFacets) { | |
| ulF1 -= countFacets; | |
| std::vector<MeshFacet>::const_iterator pF = rclFAry.begin() + ulF1; | |
| if (!pF->IsFlag(MeshFacet::INVALID)) { | |
| ulF1 = pF->_ulProp; | |
| } | |
| else { | |
| ulF1 = FACET_INDEX_MAX; | |
| } | |
| } | |
| if (ulF0 != FACET_INDEX_MAX) { | |
| unsigned short usSide = _aclFacetArray[ulF0].Side(ulP0, ulP1); | |
| assert(usSide != std::numeric_limits<unsigned short>::max()); | |
| _aclFacetArray[ulF0]._aulNeighbours[usSide] = ulF1; | |
| } | |
| if (ulF1 != FACET_INDEX_MAX) { | |
| unsigned short usSide = _aclFacetArray[ulF1].Side(ulP0, ulP1); | |
| assert(usSide != std::numeric_limits<unsigned short>::max()); | |
| _aclFacetArray[ulF1]._aulNeighbours[usSide] = ulF0; | |
| } | |
| } | |
| } | |
| return _aclFacetArray.size(); | |
| } | |
| unsigned long MeshKernel::AddFacets( | |
| const std::vector<MeshFacet>& rclFAry, | |
| const std::vector<Base::Vector3f>& rclPAry, | |
| bool checkManifolds | |
| ) | |
| { | |
| for (auto it : rclPAry) { | |
| _clBoundBox.Add(it); | |
| } | |
| this->_aclPointArray.insert(this->_aclPointArray.end(), rclPAry.begin(), rclPAry.end()); | |
| return this->AddFacets(rclFAry, checkManifolds); | |
| } | |
| void MeshKernel::Merge(const MeshKernel& rKernel) | |
| { | |
| if (this != &rKernel) { | |
| const MeshPointArray& rPoints = rKernel._aclPointArray; | |
| const MeshFacetArray& rFacets = rKernel._aclFacetArray; | |
| Merge(rPoints, rFacets); | |
| } | |
| } | |
| void MeshKernel::Merge(const MeshPointArray& rPoints, const MeshFacetArray& rFaces) | |
| { | |
| if (rPoints.empty() || rFaces.empty()) { | |
| return; // nothing to do | |
| } | |
| std::vector<PointIndex> increments(rPoints.size()); | |
| FacetIndex countFacets = this->_aclFacetArray.size(); | |
| // Reserve the additional memory to append the new facets | |
| this->_aclFacetArray.reserve(this->_aclFacetArray.size() + rFaces.size()); | |
| // Copy the new faces immediately to the facet array | |
| MeshFacet face; | |
| for (const auto& it : rFaces) { | |
| face = it; | |
| for (PointIndex point : it._aulPoints) { | |
| increments[point]++; | |
| } | |
| // append to the facet array | |
| this->_aclFacetArray.push_back(face); | |
| } | |
| std::size_t countNewPoints | |
| = std::count_if(increments.begin(), increments.end(), [](PointIndex v) { return v > 0; }); | |
| // Reserve the additional memory to append the new points | |
| PointIndex index = this->_aclPointArray.size(); | |
| this->_aclPointArray.reserve(this->_aclPointArray.size() + countNewPoints); | |
| // Now we can start inserting the points and adjust the point indices of the faces | |
| for (auto it = increments.begin(); it != increments.end(); ++it) { | |
| if (*it > 0) { | |
| // set the index of the point array | |
| *it = index++; | |
| const MeshPoint& rPt = rPoints[it - increments.begin()]; | |
| this->_aclPointArray.push_back(rPt); | |
| _clBoundBox.Add(rPt); | |
| } | |
| } | |
| for (auto pF = this->_aclFacetArray.begin() + countFacets; pF != this->_aclFacetArray.end(); | |
| ++pF) { | |
| for (PointIndex& index : pF->_aulPoints) { | |
| index = increments[index]; | |
| } | |
| } | |
| // Since rFaces could consist of a subset of the actual facet array the | |
| // neighbour indices could be totally wrong so they must be rebuilt from | |
| // scratch. Fortunately, this needs only to be done for the newly inserted | |
| // facets -- not for all | |
| RebuildNeighbours(countFacets); | |
| } | |
| void MeshKernel::Cleanup() | |
| { | |
| MeshCleanup meshCleanup(_aclPointArray, _aclFacetArray); | |
| meshCleanup.RemoveInvalids(); | |
| } | |
| void MeshKernel::Clear() | |
| { | |
| _aclPointArray.clear(); | |
| _aclFacetArray.clear(); | |
| // release memory | |
| MeshPointArray().swap(_aclPointArray); | |
| MeshFacetArray().swap(_aclFacetArray); | |
| _clBoundBox.SetVoid(); | |
| } | |
| bool MeshKernel::DeleteFacet(const MeshFacetIterator& rclIter) | |
| { | |
| FacetIndex ulNFacet {}, ulInd {}; | |
| if (rclIter._clIter >= _aclFacetArray.end()) { | |
| return false; | |
| } | |
| // index of the facet to delete | |
| ulInd = rclIter._clIter - _aclFacetArray.begin(); | |
| // invalidate neighbour indices of the neighbour facet to this facet | |
| for (FacetIndex nbIndex : rclIter._clIter->_aulNeighbours) { | |
| ulNFacet = nbIndex; | |
| if (ulNFacet != FACET_INDEX_MAX) { | |
| for (FacetIndex& nbOfNb : _aclFacetArray[ulNFacet]._aulNeighbours) { | |
| if (nbOfNb == ulInd) { | |
| nbOfNb = FACET_INDEX_MAX; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| // erase corner point if needed | |
| for (int i = 0; i < 3; i++) { | |
| if ((rclIter._clIter->_aulNeighbours[i] == FACET_INDEX_MAX) | |
| && (rclIter._clIter->_aulNeighbours[(i + 1) % 3] == FACET_INDEX_MAX)) { | |
| // no neighbours, possibly delete point | |
| ErasePoint(rclIter._clIter->_aulPoints[(i + 1) % 3], ulInd); | |
| } | |
| } | |
| // remove facet from array | |
| _aclFacetArray.Erase(_aclFacetArray.begin() + rclIter.Position()); | |
| return true; | |
| } | |
| bool MeshKernel::DeleteFacet(FacetIndex ulInd) | |
| { | |
| if (ulInd >= _aclFacetArray.size()) { | |
| return false; | |
| } | |
| MeshFacetIterator clIter(*this); | |
| clIter.Set(ulInd); | |
| return DeleteFacet(clIter); | |
| } | |
| void MeshKernel::DeleteFacets(const std::vector<FacetIndex>& raulFacets) | |
| { | |
| _aclPointArray.SetProperty(0); | |
| // number of referencing facets per point | |
| for (const auto& pF : _aclFacetArray) { | |
| _aclPointArray[pF._aulPoints[0]]._ulProp++; | |
| _aclPointArray[pF._aulPoints[1]]._ulProp++; | |
| _aclPointArray[pF._aulPoints[2]]._ulProp++; | |
| } | |
| // invalidate facet and adjust number of point references | |
| _aclFacetArray.ResetInvalid(); | |
| for (FacetIndex index : raulFacets) { | |
| MeshFacet& rclFacet = _aclFacetArray[index]; | |
| rclFacet.SetInvalid(); | |
| _aclPointArray[rclFacet._aulPoints[0]]._ulProp--; | |
| _aclPointArray[rclFacet._aulPoints[1]]._ulProp--; | |
| _aclPointArray[rclFacet._aulPoints[2]]._ulProp--; | |
| } | |
| // invalidate all unreferenced points | |
| _aclPointArray.ResetInvalid(); | |
| for (auto& pP : _aclPointArray) { | |
| if (pP._ulProp == 0) { | |
| pP.SetInvalid(); | |
| } | |
| } | |
| RemoveInvalids(); | |
| RecalcBoundBox(); | |
| } | |
| bool MeshKernel::DeletePoint(PointIndex ulInd) | |
| { | |
| if (ulInd >= _aclPointArray.size()) { | |
| return false; | |
| } | |
| MeshPointIterator clIter(*this); | |
| clIter.Set(ulInd); | |
| return DeletePoint(clIter); | |
| } | |
| bool MeshKernel::DeletePoint(const MeshPointIterator& rclIter) | |
| { | |
| MeshFacetIterator pFIter(*this), pFEnd(*this); | |
| std::vector<MeshFacetIterator> clToDel; | |
| PointIndex ulInd {}; | |
| // index of the point to delete | |
| ulInd = rclIter._clIter - _aclPointArray.begin(); | |
| pFIter.Begin(); | |
| pFEnd.End(); | |
| // check corner points of all facets | |
| while (pFIter < pFEnd) { | |
| for (PointIndex ptIndex : pFIter._clIter->_aulPoints) { | |
| if (ulInd == ptIndex) { | |
| clToDel.push_back(pFIter); | |
| } | |
| } | |
| ++pFIter; | |
| } | |
| // iterators (facets) sort by index | |
| std::sort(clToDel.begin(), clToDel.end()); | |
| // delete each facet separately (from back to front to avoid to | |
| // invalidate the iterators) | |
| for (size_t i = clToDel.size(); i > 0; i--) { | |
| DeleteFacet(clToDel[i - 1]); | |
| } | |
| return true; | |
| } | |
| void MeshKernel::DeletePoints(const std::vector<PointIndex>& raulPoints) | |
| { | |
| _aclPointArray.ResetInvalid(); | |
| for (PointIndex ptIndex : raulPoints) { | |
| _aclPointArray[ptIndex].SetInvalid(); | |
| } | |
| // delete facets if at least one corner point is invalid | |
| _aclPointArray.SetProperty(0); | |
| for (auto& pF : _aclFacetArray) { | |
| MeshPoint& rclP0 = _aclPointArray[pF._aulPoints[0]]; | |
| MeshPoint& rclP1 = _aclPointArray[pF._aulPoints[1]]; | |
| MeshPoint& rclP2 = _aclPointArray[pF._aulPoints[2]]; | |
| if (!rclP0.IsValid() || !rclP1.IsValid() || !rclP2.IsValid()) { | |
| pF.SetInvalid(); | |
| } | |
| else { | |
| pF.ResetInvalid(); | |
| rclP0._ulProp++; | |
| rclP1._ulProp++; | |
| rclP2._ulProp++; | |
| } | |
| } | |
| // invalidate all unreferenced points to delete them | |
| for (auto& pP : _aclPointArray) { | |
| if (pP._ulProp == 0) { | |
| pP.SetInvalid(); | |
| } | |
| } | |
| RemoveInvalids(); | |
| RecalcBoundBox(); | |
| } | |
| void MeshKernel::ErasePoint(PointIndex ulIndex, FacetIndex ulFacetIndex, bool bOnlySetInvalid) | |
| { | |
| std::vector<MeshFacet>::iterator pFIter, pFEnd, pFNot; | |
| pFIter = _aclFacetArray.begin(); | |
| pFNot = _aclFacetArray.begin() + ulFacetIndex; | |
| pFEnd = _aclFacetArray.end(); | |
| // check all facets | |
| while (pFIter < pFNot) { | |
| for (PointIndex ptIndex : pFIter->_aulPoints) { | |
| if (ptIndex == ulIndex) { | |
| return; // point still referenced ==> do not delete | |
| } | |
| } | |
| ++pFIter; | |
| } | |
| ++pFIter; | |
| while (pFIter < pFEnd) { | |
| for (PointIndex ptIndex : pFIter->_aulPoints) { | |
| if (ptIndex == ulIndex) { | |
| return; // point still referenced ==> do not delete | |
| } | |
| } | |
| ++pFIter; | |
| } | |
| if (!bOnlySetInvalid) { | |
| // completely remove point | |
| _aclPointArray.erase(_aclPointArray.begin() + ulIndex); | |
| // correct point indices of the facets | |
| pFIter = _aclFacetArray.begin(); | |
| while (pFIter < pFEnd) { | |
| for (PointIndex& ptIndex : pFIter->_aulPoints) { | |
| if (ptIndex > ulIndex) { | |
| ptIndex--; | |
| } | |
| } | |
| ++pFIter; | |
| } | |
| } | |
| else { // only invalidate | |
| _aclPointArray[ulIndex].SetInvalid(); | |
| } | |
| } | |
| void MeshKernel::RemoveInvalids() | |
| { | |
| std::vector<unsigned long> aulDecrements; | |
| std::vector<unsigned long>::iterator pDIter; | |
| unsigned long ulDec {}; | |
| MeshPointArray::_TIterator pPIter, pPEnd; | |
| MeshFacetArray::_TIterator pFIter, pFEnd; | |
| // generate array of decrements | |
| aulDecrements.resize(_aclPointArray.size()); | |
| pDIter = aulDecrements.begin(); | |
| ulDec = 0; | |
| pPEnd = _aclPointArray.end(); | |
| for (pPIter = _aclPointArray.begin(); pPIter != pPEnd; ++pPIter) { | |
| *pDIter++ = ulDec; | |
| if (!pPIter->IsValid()) { | |
| ulDec++; | |
| } | |
| } | |
| // correct point indices of the facets | |
| pFEnd = _aclFacetArray.end(); | |
| for (pFIter = _aclFacetArray.begin(); pFIter != pFEnd; ++pFIter) { | |
| if (pFIter->IsValid()) { | |
| pFIter->_aulPoints[0] -= aulDecrements[pFIter->_aulPoints[0]]; | |
| pFIter->_aulPoints[1] -= aulDecrements[pFIter->_aulPoints[1]]; | |
| pFIter->_aulPoints[2] -= aulDecrements[pFIter->_aulPoints[2]]; | |
| } | |
| } | |
| // delete point, number of valid points | |
| unsigned long ulNewPts | |
| = std::count_if(_aclPointArray.begin(), _aclPointArray.end(), [](const MeshPoint& p) { | |
| return p.IsValid(); | |
| }); | |
| // tmp. point array | |
| MeshPointArray aclTempPt(ulNewPts); | |
| MeshPointArray::_TIterator pPTemp = aclTempPt.begin(); | |
| pPEnd = _aclPointArray.end(); | |
| for (pPIter = _aclPointArray.begin(); pPIter != pPEnd; ++pPIter) { | |
| if (pPIter->IsValid()) { | |
| *pPTemp++ = *pPIter; | |
| } | |
| } | |
| // free memory | |
| //_aclPointArray = aclTempPt; | |
| // aclTempPt.clear(); | |
| _aclPointArray.swap(aclTempPt); | |
| MeshPointArray().swap(aclTempPt); | |
| // generate array of facet decrements | |
| aulDecrements.resize(_aclFacetArray.size()); | |
| pDIter = aulDecrements.begin(); | |
| ulDec = 0; | |
| pFEnd = _aclFacetArray.end(); | |
| for (pFIter = _aclFacetArray.begin(); pFIter != pFEnd; ++pFIter, ++pDIter) { | |
| *pDIter = ulDec; | |
| if (!pFIter->IsValid()) { | |
| ulDec++; | |
| } | |
| } | |
| // correct neighbour indices of the facets | |
| pFEnd = _aclFacetArray.end(); | |
| for (pFIter = _aclFacetArray.begin(); pFIter != pFEnd; ++pFIter) { | |
| if (pFIter->IsValid()) { | |
| for (FacetIndex& nbIndex : pFIter->_aulNeighbours) { | |
| FacetIndex k = nbIndex; | |
| if (k != FACET_INDEX_MAX) { | |
| if (_aclFacetArray[k].IsValid()) { | |
| nbIndex -= aulDecrements[k]; | |
| } | |
| else { | |
| nbIndex = FACET_INDEX_MAX; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // delete facets, number of valid facets | |
| unsigned long ulDelFacets | |
| = std::count_if(_aclFacetArray.begin(), _aclFacetArray.end(), [](const MeshFacet& f) { | |
| return f.IsValid(); | |
| }); | |
| MeshFacetArray aclFArray(ulDelFacets); | |
| MeshFacetArray::_TIterator pFTemp = aclFArray.begin(); | |
| pFEnd = _aclFacetArray.end(); | |
| for (pFIter = _aclFacetArray.begin(); pFIter != pFEnd; ++pFIter) { | |
| if (pFIter->IsValid()) { | |
| *pFTemp++ = *pFIter; | |
| } | |
| } | |
| // free memory | |
| //_aclFacetArray = aclFArray; | |
| _aclFacetArray.swap(aclFArray); | |
| } | |
| void MeshKernel::CutFacets( | |
| const MeshFacetGrid& rclGrid, | |
| const Base::ViewProjMethod* pclProj, | |
| const Base::Polygon2d& rclPoly, | |
| bool bCutInner, | |
| std::vector<MeshGeomFacet>& raclFacets | |
| ) | |
| { | |
| std::vector<FacetIndex> aulFacets; | |
| MeshAlgorithm(*this).CheckFacets(rclGrid, pclProj, rclPoly, bCutInner, aulFacets); | |
| for (FacetIndex it : aulFacets) { | |
| raclFacets.push_back(GetFacet(it)); | |
| } | |
| DeleteFacets(aulFacets); | |
| } | |
| void MeshKernel::CutFacets( | |
| const MeshFacetGrid& grid, | |
| const Base::ViewProjMethod* proj, | |
| const Base::Polygon2d& poly, | |
| bool bInner, | |
| std::vector<FacetIndex>& cut | |
| ) | |
| { | |
| MeshAlgorithm(*this).CheckFacets(grid, proj, poly, bInner, cut); | |
| DeleteFacets(cut); | |
| } | |
| std::vector<PointIndex> MeshKernel::GetFacetPoints(const std::vector<FacetIndex>& facets) const | |
| { | |
| std::vector<PointIndex> points; | |
| for (FacetIndex it : facets) { | |
| PointIndex p0 {}, p1 {}, p2 {}; | |
| GetFacetPoints(it, p0, p1, p2); | |
| points.push_back(p0); | |
| points.push_back(p1); | |
| points.push_back(p2); | |
| } | |
| std::sort(points.begin(), points.end()); | |
| points.erase(std::unique(points.begin(), points.end()), points.end()); | |
| return points; | |
| } | |
| std::vector<FacetIndex> MeshKernel::GetPointFacets(const std::vector<PointIndex>& points) const | |
| { | |
| _aclPointArray.ResetFlag(MeshPoint::TMP0); | |
| _aclFacetArray.ResetFlag(MeshFacet::TMP0); | |
| for (PointIndex point : points) { | |
| _aclPointArray[point].SetFlag(MeshPoint::TMP0); | |
| } | |
| // mark facets if at least one corner point is marked | |
| for (const auto& pF : _aclFacetArray) { | |
| const MeshPoint& rclP0 = _aclPointArray[pF._aulPoints[0]]; | |
| const MeshPoint& rclP1 = _aclPointArray[pF._aulPoints[1]]; | |
| const MeshPoint& rclP2 = _aclPointArray[pF._aulPoints[2]]; | |
| if (rclP0.IsFlag(MeshPoint::TMP0) || rclP1.IsFlag(MeshPoint::TMP0) | |
| || rclP2.IsFlag(MeshPoint::TMP0)) { | |
| pF.SetFlag(MeshFacet::TMP0); | |
| } | |
| } | |
| std::vector<FacetIndex> facets; | |
| MeshAlgorithm(*this).GetFacetsFlag(facets, MeshFacet::TMP0); | |
| return facets; | |
| } | |
| std::vector<FacetIndex> MeshKernel::HasFacets(const MeshPointIterator& rclIter) const | |
| { | |
| PointIndex ulPtInd = rclIter.Position(); | |
| std::vector<MeshFacet>::const_iterator pFIter = _aclFacetArray.begin(); | |
| std::vector<MeshFacet>::const_iterator pFBegin = _aclFacetArray.begin(); | |
| std::vector<MeshFacet>::const_iterator pFEnd = _aclFacetArray.end(); | |
| std::vector<FacetIndex> aulBelongs; | |
| while (pFIter < pFEnd) { | |
| for (PointIndex point : pFIter->_aulPoints) { | |
| if (point == ulPtInd) { | |
| aulBelongs.push_back(pFIter - pFBegin); | |
| break; | |
| } | |
| } | |
| ++pFIter; | |
| } | |
| return aulBelongs; | |
| } | |
| MeshPointArray MeshKernel::GetPoints(const std::vector<PointIndex>& indices) const | |
| { | |
| MeshPointArray ary; | |
| ary.reserve(indices.size()); | |
| for (PointIndex it : indices) { | |
| ary.push_back(this->_aclPointArray[it]); | |
| } | |
| return ary; | |
| } | |
| MeshFacetArray MeshKernel::GetFacets(const std::vector<FacetIndex>& indices) const | |
| { | |
| MeshFacetArray ary; | |
| ary.reserve(indices.size()); | |
| for (FacetIndex it : indices) { | |
| ary.push_back(this->_aclFacetArray[it]); | |
| } | |
| return ary; | |
| } | |
| void MeshKernel::Write(std::ostream& rclOut) const | |
| { | |
| if (!rclOut || rclOut.bad()) { | |
| return; | |
| } | |
| Base::OutputStream str(rclOut); | |
| // Write a header with a "magic number" and a version | |
| str << static_cast<uint32_t>(0xA0B0C0D0); | |
| str << static_cast<uint32_t>(0x010000); | |
| char szInfo[257]; // needs an additional byte for zero-termination | |
| strcpy( | |
| szInfo, | |
| "MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-" | |
| "MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-" | |
| "MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-" | |
| "MESH-MESH-MESH-\n" | |
| ); | |
| rclOut.write(szInfo, 256); | |
| // write the number of points and facets | |
| str << static_cast<uint32_t>(CountPoints()) << static_cast<uint32_t>(CountFacets()); | |
| // write the data | |
| for (const auto& it : _aclPointArray) { | |
| str << it.x << it.y << it.z; | |
| } | |
| for (const auto& it : _aclFacetArray) { | |
| str << static_cast<uint32_t>(it._aulPoints[0]) << static_cast<uint32_t>(it._aulPoints[1]) | |
| << static_cast<uint32_t>(it._aulPoints[2]); | |
| str << static_cast<uint32_t>(it._aulNeighbours[0]) | |
| << static_cast<uint32_t>(it._aulNeighbours[1]) | |
| << static_cast<uint32_t>(it._aulNeighbours[2]); | |
| } | |
| str << _clBoundBox.MinX << _clBoundBox.MaxX; | |
| str << _clBoundBox.MinY << _clBoundBox.MaxY; | |
| str << _clBoundBox.MinZ << _clBoundBox.MaxZ; | |
| } | |
| void MeshKernel::Read(std::istream& rclIn) | |
| { | |
| if (!rclIn || rclIn.bad()) { | |
| return; | |
| } | |
| // get header | |
| Base::InputStream str(rclIn); | |
| // Read the header with a "magic number" and a version | |
| uint32_t magic {}, version {}, swap_magic {}, swap_version {}; | |
| str >> magic >> version; | |
| swap_magic = magic; | |
| Base::SwapEndian(swap_magic); | |
| swap_version = version; | |
| Base::SwapEndian(swap_version); | |
| uint32_t open_edge = 0xffffffff; // value to mark an open edge | |
| // is it the new or old format? | |
| bool new_format = false; | |
| if (magic == 0xA0B0C0D0 && version == 0x010000) { | |
| new_format = true; | |
| } | |
| else if (swap_magic == 0xA0B0C0D0 && swap_version == 0x010000) { | |
| new_format = true; | |
| str.setByteOrder(Base::Stream::BigEndian); | |
| } | |
| if (new_format) { | |
| char szInfo[256]; | |
| rclIn.read(szInfo, 256); | |
| // read the number of points and facets | |
| uint32_t uCtPts = 0, uCtFts = 0; | |
| str >> uCtPts >> uCtFts; | |
| try { | |
| // read the data | |
| MeshPointArray pointArray; | |
| pointArray.resize(uCtPts); | |
| for (auto& it : pointArray) { | |
| str >> it.x >> it.y >> it.z; | |
| } | |
| MeshFacetArray facetArray; | |
| facetArray.resize(uCtFts); | |
| uint32_t v1 {}, v2 {}, v3 {}; | |
| for (auto& it : facetArray) { | |
| str >> v1 >> v2 >> v3; | |
| // make sure to have valid indices | |
| if (v1 >= uCtPts || v2 >= uCtPts || v3 >= uCtPts) { | |
| throw Base::BadFormatError("Invalid data structure"); | |
| } | |
| it._aulPoints[0] = v1; | |
| it._aulPoints[1] = v2; | |
| it._aulPoints[2] = v3; | |
| // On systems where an 'unsigned long' is a 64-bit value | |
| // the empty neighbour must be explicitly set to 'FACET_INDEX_MAX' | |
| // because in algorithms this value is always used to check | |
| // for open edges. | |
| str >> v1 >> v2 >> v3; | |
| // make sure to have valid indices | |
| if (v1 >= uCtFts && v1 < open_edge) { | |
| throw Base::BadFormatError("Invalid data structure"); | |
| } | |
| if (v2 >= uCtFts && v2 < open_edge) { | |
| throw Base::BadFormatError("Invalid data structure"); | |
| } | |
| if (v3 >= uCtFts && v3 < open_edge) { | |
| throw Base::BadFormatError("Invalid data structure"); | |
| } | |
| if (v1 < open_edge) { | |
| it._aulNeighbours[0] = v1; | |
| } | |
| else { | |
| it._aulNeighbours[0] = FACET_INDEX_MAX; | |
| } | |
| if (v2 < open_edge) { | |
| it._aulNeighbours[1] = v2; | |
| } | |
| else { | |
| it._aulNeighbours[1] = FACET_INDEX_MAX; | |
| } | |
| if (v3 < open_edge) { | |
| it._aulNeighbours[2] = v3; | |
| } | |
| else { | |
| it._aulNeighbours[2] = FACET_INDEX_MAX; | |
| } | |
| } | |
| str >> _clBoundBox.MinX >> _clBoundBox.MaxX; | |
| str >> _clBoundBox.MinY >> _clBoundBox.MaxY; | |
| str >> _clBoundBox.MinZ >> _clBoundBox.MaxZ; | |
| // If we reach this block no exception occurred and we can safely assign the mesh | |
| _aclPointArray.swap(pointArray); | |
| _aclFacetArray.swap(facetArray); | |
| } | |
| catch (std::exception&) { | |
| // Special handling of std::length_error | |
| throw Base::BadFormatError("Reading from stream failed"); | |
| } | |
| } | |
| else { | |
| // The old formats | |
| unsigned long uCtPts = magic, uCtFts = version; | |
| MeshPointArray pointArray; | |
| MeshFacetArray facetArray; | |
| // Sanity checks so we don't over-allocate below: limit the mesh to 1 billion points and | |
| // 1 billion facets. Coverity issue 515697. | |
| if (uCtPts > 1e9 || uCtFts > 1e9) { | |
| throw Base::BadFormatError("Mesh seems to have over a billion points or facets"); | |
| } | |
| float ratio = 0; | |
| if (uCtPts > 0) { | |
| ratio = static_cast<float>(uCtFts) / static_cast<float>(uCtPts); | |
| } | |
| // without edge array | |
| if (ratio < 2.5F) { | |
| // the stored mesh kernel might be empty | |
| if (uCtPts > 0) { | |
| pointArray.resize(uCtPts); | |
| rclIn.read((char*)pointArray.data(), uCtPts * sizeof(MeshPoint)); | |
| } | |
| if (uCtFts > 0) { | |
| facetArray.resize(uCtFts); | |
| rclIn.read((char*)facetArray.data(), uCtFts * sizeof(MeshFacet)); | |
| } | |
| rclIn.read((char*)&_clBoundBox, sizeof(Base::BoundBox3f)); | |
| } | |
| else { | |
| // with edge array | |
| unsigned long uCtEdges = uCtFts; | |
| str >> magic; | |
| uCtFts = magic; | |
| pointArray.resize(uCtPts); | |
| for (auto& it : pointArray) { | |
| str >> it.x >> it.y >> it.z; | |
| } | |
| uint32_t dummy {}; | |
| for (unsigned long i = 0; i < uCtEdges; i++) { | |
| str >> dummy; | |
| } | |
| uint32_t v1 {}, v2 {}, v3 {}; | |
| facetArray.resize(uCtFts); | |
| for (auto& it : facetArray) { | |
| str >> v1 >> v2 >> v3; | |
| it._aulNeighbours[0] = v1; | |
| it._aulNeighbours[1] = v2; | |
| it._aulNeighbours[2] = v3; | |
| str >> v1 >> v2 >> v3; | |
| it._aulPoints[0] = v1; | |
| it._aulPoints[1] = v2; | |
| it._aulPoints[2] = v3; | |
| str >> it._ucFlag; | |
| } | |
| str >> _clBoundBox.MinX >> _clBoundBox.MinY >> _clBoundBox.MinZ >> _clBoundBox.MaxX | |
| >> _clBoundBox.MaxY >> _clBoundBox.MaxZ; | |
| } | |
| for (auto& it : facetArray) { | |
| for (int i = 0; i < 3; i++) { | |
| if (it._aulPoints[i] >= uCtPts) { | |
| throw Base::BadFormatError("Invalid data structure"); | |
| } | |
| if (it._aulNeighbours[i] < FACET_INDEX_MAX && it._aulNeighbours[i] >= uCtFts) { | |
| throw Base::BadFormatError("Invalid data structure"); | |
| } | |
| } | |
| } | |
| _aclPointArray.swap(pointArray); | |
| _aclFacetArray.swap(facetArray); | |
| } | |
| } | |
| void MeshKernel::operator*=(const Base::Matrix4D& rclMat) | |
| { | |
| this->Transform(rclMat); | |
| } | |
| void MeshKernel::Transform(const Base::Matrix4D& rclMat) | |
| { | |
| auto clPIter = _aclPointArray.begin(), clPEIter = _aclPointArray.end(); | |
| _clBoundBox.SetVoid(); | |
| while (clPIter < clPEIter) { | |
| *clPIter *= rclMat; | |
| _clBoundBox.Add(*clPIter); | |
| clPIter++; | |
| } | |
| } | |
| void MeshKernel::Smooth(int iterations, float stepsize) | |
| { | |
| (void)stepsize; | |
| LaplaceSmoothing(*this).Smooth(iterations); | |
| } | |
| void MeshKernel::RecalcBoundBox() const | |
| { | |
| _clBoundBox.SetVoid(); | |
| for (const auto& pI : _aclPointArray) { | |
| _clBoundBox.Add(pI); | |
| } | |
| } | |
| std::vector<Base::Vector3f> MeshKernel::CalcVertexNormals() const | |
| { | |
| std::vector<Base::Vector3f> normals; | |
| normals.resize(CountPoints()); | |
| PointIndex p1 {}, p2 {}, p3 {}; | |
| unsigned int ct = CountFacets(); | |
| for (unsigned int pFIter = 0; pFIter < ct; pFIter++) { | |
| GetFacetPoints(pFIter, p1, p2, p3); | |
| Base::Vector3f Norm = (GetPoint(p2) - GetPoint(p1)) % (GetPoint(p3) - GetPoint(p1)); | |
| normals[p1] += Norm; | |
| normals[p2] += Norm; | |
| normals[p3] += Norm; | |
| } | |
| return normals; | |
| } | |
| std::vector<Base::Vector3f> MeshKernel::GetFacetNormals(const std::vector<FacetIndex>& facets) const | |
| { | |
| std::vector<Base::Vector3f> normals; | |
| normals.reserve(facets.size()); | |
| for (FacetIndex it : facets) { | |
| const MeshFacet& face = _aclFacetArray[it]; | |
| const Base::Vector3f& p1 = _aclPointArray[face._aulPoints[0]]; | |
| const Base::Vector3f& p2 = _aclPointArray[face._aulPoints[1]]; | |
| const Base::Vector3f& p3 = _aclPointArray[face._aulPoints[2]]; | |
| Base::Vector3f n = (p2 - p1) % (p3 - p1); | |
| n.Normalize(); | |
| normals.emplace_back(n); | |
| } | |
| return normals; | |
| } | |
| // Evaluation | |
| float MeshKernel::GetSurface() const | |
| { | |
| float fSurface = 0.0; | |
| MeshFacetIterator cIter(*this); | |
| for (cIter.Init(); cIter.More(); cIter.Next()) { | |
| fSurface += cIter->Area(); | |
| } | |
| return fSurface; | |
| } | |
| float MeshKernel::GetSurface(const std::vector<FacetIndex>& aSegment) const | |
| { | |
| float fSurface = 0.0; | |
| MeshFacetIterator cIter(*this); | |
| for (FacetIndex it : aSegment) { | |
| cIter.Set(it); | |
| fSurface += cIter->Area(); | |
| } | |
| return fSurface; | |
| } | |
| float MeshKernel::GetVolume() const | |
| { | |
| // MeshEvalSolid cSolid(*this); | |
| // if ( !cSolid.Evaluate() ) | |
| // return 0.0f; // no solid | |
| float fVolume = 0.0; | |
| MeshFacetIterator cIter(*this); | |
| Base::Vector3f p1, p2, p3; | |
| for (cIter.Init(); cIter.More(); cIter.Next()) { | |
| const MeshGeomFacet& rclF = *cIter; | |
| p1 = rclF._aclPoints[0]; | |
| p2 = rclF._aclPoints[1]; | |
| p3 = rclF._aclPoints[2]; | |
| fVolume | |
| += (-p3.x * p2.y * p1.z + p2.x * p3.y * p1.z + p3.x * p1.y * p2.z - p1.x * p3.y * p2.z | |
| - p2.x * p1.y * p3.z + p1.x * p2.y * p3.z); | |
| } | |
| fVolume /= 6.0F; | |
| fVolume = std::fabs(fVolume); | |
| return fVolume; | |
| } | |
| bool MeshKernel::HasOpenEdges() const | |
| { | |
| MeshEvalSolid eval(*this); | |
| return !eval.Evaluate(); | |
| } | |
| bool MeshKernel::HasNonManifolds() const | |
| { | |
| MeshEvalTopology eval(*this); | |
| return !eval.Evaluate(); | |
| } | |
| bool MeshKernel::HasSelfIntersections() const | |
| { | |
| MeshEvalSelfIntersection eval(*this); | |
| return !eval.Evaluate(); | |
| } | |
| // Iterators | |
| MeshFacetIterator MeshKernel::FacetIterator() const | |
| { | |
| MeshFacetIterator it(*this); | |
| it.Begin(); | |
| return it; | |
| } | |
| MeshPointIterator MeshKernel::PointIterator() const | |
| { | |
| MeshPointIterator it(*this); | |
| it.Begin(); | |
| return it; | |
| } | |
| void MeshKernel::GetEdges(std::vector<MeshGeomEdge>& edges) const | |
| { | |
| std::set<MeshBuilder::Edge> tmp; | |
| for (const auto& it : _aclFacetArray) { | |
| for (int i = 0; i < 3; i++) { | |
| tmp.insert( | |
| MeshBuilder::Edge(it._aulPoints[i], it._aulPoints[(i + 1) % 3], it._aulNeighbours[i]) | |
| ); | |
| } | |
| } | |
| edges.reserve(tmp.size()); | |
| for (const auto& it2 : tmp) { | |
| MeshGeomEdge edge; | |
| edge._aclPoints[0] = this->_aclPointArray[it2.pt1]; | |
| edge._aclPoints[1] = this->_aclPointArray[it2.pt2]; | |
| edge._bBorder = it2.facetIdx == FACET_INDEX_MAX; | |
| edges.push_back(edge); | |
| } | |
| } | |
| unsigned long MeshKernel::CountEdges() const | |
| { | |
| unsigned long openEdges = 0, closedEdges = 0; | |
| for (const auto& it : _aclFacetArray) { | |
| for (FacetIndex nbFacet : it._aulNeighbours) { | |
| if (nbFacet == FACET_INDEX_MAX) { | |
| openEdges++; | |
| } | |
| else { | |
| closedEdges++; | |
| } | |
| } | |
| } | |
| return (openEdges + (closedEdges / 2)); | |
| } | |