FreeCAD / src /Mod /Mesh /App /Core /Algorithm.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2005 Imetric 3D GmbH *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include <algorithm>
#include <limits>
#include <Base/Console.h>
#include <Base/Sequencer.h>
#include "Algorithm.h"
#include "Approximation.h"
#include "Elements.h"
#include "Grid.h"
#include "Iterator.h"
#include "Triangulation.h"
using namespace MeshCore;
using Base::BoundBox2d;
using Base::BoundBox3f;
using Base::Polygon2d;
bool MeshAlgorithm::IsVertexVisible(
const Base::Vector3f& rcVertex,
const Base::Vector3f& rcView,
const MeshFacetGrid& rclGrid
) const
{
const float fMaxDistance = 0.001F;
Base::Vector3f cDirection = rcVertex - rcView;
float fDistance = cDirection.Length();
Base::Vector3f cIntsct;
FacetIndex uInd {};
// search for the nearest facet to rcView in direction to rcVertex
if (NearestFacetOnRay(rcView, cDirection, /*1.2f*fDistance,*/ rclGrid, cIntsct, uInd)) {
// now check if the facet overlays the point
float fLen = Base::Distance(rcView, cIntsct);
if (fLen < fDistance) {
// is it the same point?
if (Base::Distance(rcVertex, cIntsct) > fMaxDistance) {
// ok facet overlays the vertex
return false;
}
}
}
return true; // no facet between the two points
}
bool MeshAlgorithm::NearestFacetOnRay(
const Base::Vector3f& rclPt,
const Base::Vector3f& rclDir,
Base::Vector3f& rclRes,
FacetIndex& rulFacet
) const
{
return NearestFacetOnRay(rclPt, rclDir, Mathf::PI, rclRes, rulFacet);
}
bool MeshAlgorithm::NearestFacetOnRay(
const Base::Vector3f& rclPt,
const Base::Vector3f& rclDir,
float fMaxAngle,
Base::Vector3f& rclRes,
FacetIndex& rulFacet
) const
{
Base::Vector3f clProj;
Base::Vector3f clRes;
bool bSol = false;
FacetIndex ulInd = 0;
// slow execution with no grid
MeshFacetIterator clFIter(_rclMesh);
for (clFIter.Init(); clFIter.More(); clFIter.Next()) {
if (clFIter->Foraminate(rclPt, rclDir, clRes, fMaxAngle)) {
if (!bSol) {
// first solution
bSol = true;
clProj = clRes;
ulInd = clFIter.Position();
}
else {
// is closer to the point
if ((clRes - rclPt).Length() < (clProj - rclPt).Length()) {
clProj = clRes;
ulInd = clFIter.Position();
}
}
}
}
if (bSol) {
rclRes = clProj;
rulFacet = ulInd;
}
return bSol;
}
bool MeshAlgorithm::NearestFacetOnRay(
const Base::Vector3f& rclPt,
const Base::Vector3f& rclDir,
const MeshFacetGrid& rclGrid,
Base::Vector3f& rclRes,
FacetIndex& rulFacet
) const
{
std::vector<FacetIndex> aulFacets;
MeshGridIterator clGridIter(rclGrid);
if (clGridIter.InitOnRay(rclPt, rclDir, aulFacets)) {
if (!RayNearestField(rclPt, rclDir, aulFacets, rclRes, rulFacet)) {
aulFacets.clear();
while (clGridIter.NextOnRay(aulFacets)) {
if (RayNearestField(rclPt, rclDir, aulFacets, rclRes, rulFacet)) {
return true;
}
}
}
else {
return true;
}
}
return false;
}
bool MeshAlgorithm::NearestFacetOnRay(
const Base::Vector3f& rclPt,
const Base::Vector3f& rclDir,
float fMaxSearchArea,
const MeshFacetGrid& rclGrid,
Base::Vector3f& rclRes,
FacetIndex& rulFacet
) const
{
const float fMaxAngle = 1.75F;
std::vector<FacetIndex> aulFacets;
MeshGridIterator clGridIter(rclGrid);
if (clGridIter.InitOnRay(rclPt, rclDir, fMaxSearchArea, aulFacets)) {
if (!RayNearestField(rclPt, rclDir, aulFacets, rclRes, rulFacet, fMaxAngle)) {
aulFacets.clear();
while (clGridIter.NextOnRay(aulFacets)) {
if (RayNearestField(rclPt, rclDir, aulFacets, rclRes, rulFacet, fMaxAngle)) {
return true;
}
}
}
else {
return true;
}
}
return false;
}
bool MeshAlgorithm::NearestFacetOnRay(
const Base::Vector3f& rclPt,
const Base::Vector3f& rclDir,
const std::vector<FacetIndex>& raulFacets,
Base::Vector3f& rclRes,
FacetIndex& rulFacet
) const
{
Base::Vector3f clProj;
Base::Vector3f clRes;
bool bSol = false;
FacetIndex ulInd = 0;
for (FacetIndex index : raulFacets) {
MeshGeomFacet rclSFacet = _rclMesh.GetFacet(index);
if (rclSFacet.Foraminate(rclPt, rclDir, clRes)) {
if (!bSol) { // first solution
bSol = true;
clProj = clRes;
ulInd = index;
}
else { // is closer to the point
if ((clRes - rclPt).Length() < (clProj - rclPt).Length()) {
clProj = clRes;
ulInd = index;
}
}
}
}
if (bSol) {
rclRes = clProj;
rulFacet = ulInd;
}
return bSol;
}
bool MeshAlgorithm::RayNearestField(
const Base::Vector3f& rclPt,
const Base::Vector3f& rclDir,
const std::vector<FacetIndex>& raulFacets,
Base::Vector3f& rclRes,
FacetIndex& rulFacet,
float /*fMaxAngle*/
) const
{
Base::Vector3f clProj, clRes;
bool bSol = false;
FacetIndex ulInd = 0;
for (FacetIndex index : raulFacets) {
if (_rclMesh.GetFacet(index).Foraminate(rclPt, rclDir, clRes /*, fMaxAngle*/)) {
if (!bSol) { // first solution
bSol = true;
clProj = clRes;
ulInd = index;
}
else { // is closer to the point
if ((clRes - rclPt).Length() < (clProj - rclPt).Length()) {
clProj = clRes;
ulInd = index;
}
}
}
}
if (bSol) {
rclRes = clProj;
rulFacet = ulInd;
}
return bSol;
}
bool MeshAlgorithm::FirstFacetToVertex(
const Base::Vector3f& rPt,
float fMaxDistance,
const MeshFacetGrid& rGrid,
FacetIndex& uIndex
) const
{
const float fEps = 0.001F;
bool found = false;
std::vector<FacetIndex> facets;
// get the facets of the grid the point lies into
rGrid.GetElements(rPt, facets);
// Check all facets inside the grid if the point is part of it
for (FacetIndex facet : facets) {
MeshGeomFacet cFacet = this->_rclMesh.GetFacet(facet);
if (cFacet.IsPointOfFace(rPt, fMaxDistance)) {
found = true;
uIndex = facet;
break;
}
// if not then check the distance to the border of the triangle
Base::Vector3f res;
float fDist {};
unsigned short uSide {};
cFacet.ProjectPointToPlane(rPt, res);
cFacet.NearestEdgeToPoint(res, fDist, uSide);
if (fDist < fEps) {
found = true;
uIndex = facet;
break;
}
}
return found;
}
float MeshAlgorithm::GetAverageEdgeLength() const
{
float fLen = 0.0F;
MeshFacetIterator cF(_rclMesh);
for (cF.Init(); cF.More(); cF.Next()) {
for (int i = 0; i < 3; i++) {
fLen += Base::Distance(cF->_aclPoints[i], cF->_aclPoints[(i + 1) % 3]);
}
}
fLen = fLen / (3.0F * _rclMesh.CountFacets());
return fLen;
}
float MeshAlgorithm::GetMinimumEdgeLength() const
{
float fLen = std::numeric_limits<float>::max();
MeshFacetIterator cF(_rclMesh);
for (cF.Init(); cF.More(); cF.Next()) {
for (int i = 0; i < 3; i++) {
fLen = std::min(fLen, Base::Distance(cF->_aclPoints[i], cF->_aclPoints[(i + 1) % 3]));
}
}
return fLen;
}
float MeshAlgorithm::GetMaximumEdgeLength() const
{
float fLen = 0.0F;
MeshFacetIterator cF(_rclMesh);
for (cF.Init(); cF.More(); cF.Next()) {
for (int i = 0; i < 3; i++) {
fLen = std::max(fLen, Base::Distance(cF->_aclPoints[i], cF->_aclPoints[(i + 1) % 3]));
}
}
return fLen;
}
Base::Vector3f MeshAlgorithm::GetGravityPoint() const
{
Base::Vector3f center;
MeshPointIterator cP(_rclMesh);
for (cP.Init(); cP.More(); cP.Next()) {
center += *cP;
}
return center / static_cast<float>(_rclMesh.CountPoints());
}
void MeshAlgorithm::GetMeshBorders(std::list<std::vector<Base::Vector3f>>& rclBorders) const
{
std::vector<FacetIndex> aulAllFacets(_rclMesh.CountFacets());
FacetIndex k = 0;
for (FacetIndex& index : aulAllFacets) {
index = k++;
}
GetFacetBorders(aulAllFacets, rclBorders);
}
void MeshAlgorithm::GetMeshBorders(std::list<std::vector<PointIndex>>& rclBorders) const
{
std::vector<FacetIndex> aulAllFacets(_rclMesh.CountFacets());
FacetIndex k = 0;
for (FacetIndex& index : aulAllFacets) {
index = k++;
}
GetFacetBorders(aulAllFacets, rclBorders, true);
}
void MeshAlgorithm::GetFacetBorders(
const std::vector<FacetIndex>& raulInd,
std::list<std::vector<Base::Vector3f>>& rclBorders
) const
{
const MeshPointArray& rclPAry = _rclMesh._aclPointArray;
std::list<std::vector<PointIndex>> aulBorders;
GetFacetBorders(raulInd, aulBorders, true);
for (const auto& border : aulBorders) {
std::vector<Base::Vector3f> boundary;
boundary.reserve(border.size());
for (PointIndex jt : border) {
boundary.push_back(rclPAry[jt]);
}
rclBorders.push_back(boundary);
}
}
void MeshAlgorithm::GetFacetBorders(
const std::vector<FacetIndex>& raulInd,
std::list<std::vector<PointIndex>>& rclBorders,
bool ignoreOrientation
) const
{
const MeshFacetArray& rclFAry = _rclMesh._aclFacetArray;
// mark all facets that are in the indices list
ResetFacetFlag(MeshFacet::VISIT);
for (FacetIndex it : raulInd) {
rclFAry[it].SetFlag(MeshFacet::VISIT);
}
// collect all boundary edges (unsorted)
std::list<std::pair<PointIndex, PointIndex>> aclEdges;
for (FacetIndex it : raulInd) {
const MeshFacet& rclFacet = rclFAry[it];
for (unsigned short i = 0; i < 3; i++) {
FacetIndex ulNB = rclFacet._aulNeighbours[i];
if (ulNB != FACET_INDEX_MAX) {
if (rclFAry[ulNB].IsFlag(MeshFacet::VISIT)) {
continue;
}
}
aclEdges.push_back(rclFacet.GetEdge(i));
}
}
if (aclEdges.empty()) {
return; // no borders found (=> solid)
}
// search for edges in the unsorted list
PointIndex ulFirst {}, ulLast {};
std::list<PointIndex> clBorder;
ulFirst = aclEdges.begin()->first;
ulLast = aclEdges.begin()->second;
aclEdges.erase(aclEdges.begin());
clBorder.push_back(ulFirst);
clBorder.push_back(ulLast);
while (!aclEdges.empty()) {
// get adjacent edge
std::list<std::pair<PointIndex, PointIndex>>::iterator pEI;
for (pEI = aclEdges.begin(); pEI != aclEdges.end(); ++pEI) {
if (pEI->first == ulLast) {
ulLast = pEI->second;
clBorder.push_back(ulLast);
aclEdges.erase(pEI);
pEI = aclEdges.begin();
break;
}
if (pEI->second == ulFirst) {
ulFirst = pEI->first;
clBorder.push_front(ulFirst);
aclEdges.erase(pEI);
pEI = aclEdges.begin();
break;
}
// Note: Using this might result into boundaries with wrong orientation.
// But if the mesh has some facets with wrong orientation we might get
// broken boundary curves.
if (pEI->second == ulLast && ignoreOrientation) {
ulLast = pEI->first;
clBorder.push_back(ulLast);
aclEdges.erase(pEI);
pEI = aclEdges.begin();
break;
}
if (pEI->first == ulFirst && ignoreOrientation) {
ulFirst = pEI->second;
clBorder.push_front(ulFirst);
aclEdges.erase(pEI);
pEI = aclEdges.begin();
break;
}
}
// Note: Calling erase on list iterators doesn't force a re-allocation and
// thus doesn't invalidate the iterator itself, only the referenced object
if ((pEI == aclEdges.end()) || aclEdges.empty() || (ulLast == ulFirst)) {
// no further edge found or closed polyline, respectively
rclBorders.emplace_back(clBorder.begin(), clBorder.end());
clBorder.clear();
if (!aclEdges.empty()) {
// start new boundary
ulFirst = aclEdges.begin()->first;
ulLast = aclEdges.begin()->second;
aclEdges.erase(aclEdges.begin());
clBorder.push_back(ulFirst);
clBorder.push_back(ulLast);
}
}
}
}
void MeshAlgorithm::GetFacetBorder(FacetIndex uFacet, std::list<PointIndex>& rBorder) const
{
const MeshFacetArray& rFAry = _rclMesh._aclFacetArray;
std::list<std::pair<PointIndex, PointIndex>> openEdges;
if (uFacet >= rFAry.size()) {
return;
}
// add the open edge to the beginning of the list
auto face = rFAry.begin() + uFacet;
for (unsigned short i = 0; i < 3; i++) {
if (face->_aulNeighbours[i] == FACET_INDEX_MAX) {
openEdges.push_back(face->GetEdge(i));
}
}
if (openEdges.empty()) {
return; // facet is not a border facet
}
for (auto it = rFAry.begin(); it != rFAry.end(); ++it) {
if (it == face) {
continue;
}
for (unsigned short i = 0; i < 3; i++) {
if (it->_aulNeighbours[i] == FACET_INDEX_MAX) {
openEdges.push_back(it->GetEdge(i));
}
}
}
SplitBoundaryFromOpenEdges(openEdges, rBorder);
}
void MeshAlgorithm::GetFacetsBorders(
const std::vector<FacetIndex>& uFacets,
std::list<std::vector<PointIndex>>& rBorders
) const
{
ResetFacetFlag(MeshFacet::TMP0);
SetFacetsFlag(uFacets, MeshFacet::TMP0);
ResetPointFlag(MeshPoint::TMP0);
const MeshFacetArray& rFAry = _rclMesh._aclFacetArray;
const MeshPointArray& rPAry = _rclMesh._aclPointArray;
std::list<std::pair<PointIndex, PointIndex>> openEdges;
// add the open edge to the beginning of the list
for (auto it : uFacets) {
const MeshFacet& face = rFAry[it];
for (int i = 0; i < 3; i++) {
if (face._aulNeighbours[i] == FACET_INDEX_MAX) {
std::pair<PointIndex, PointIndex> openEdge = face.GetEdge(i);
openEdges.push_back(openEdge);
// mark all points of open edges of the given facets
rPAry[openEdge.first].SetFlag(MeshPoint::TMP0);
rPAry[openEdge.second].SetFlag(MeshPoint::TMP0);
}
}
}
if (openEdges.empty()) {
return; // none of the facets are border facets
}
for (const auto& it : rFAry) {
if (it.IsFlag(MeshFacet::TMP0)) {
continue;
}
for (int i = 0; i < 3; i++) {
if (it._aulNeighbours[i] == FACET_INDEX_MAX) {
openEdges.push_back(it.GetEdge(i));
}
}
}
// if the first element is not an edge of "uFacets" then give up
while (!openEdges.empty()) {
PointIndex first = openEdges.begin()->first;
PointIndex second = openEdges.begin()->second;
if (!rPAry[first].IsFlag(MeshPoint::TMP0)) {
break;
}
if (!rPAry[second].IsFlag(MeshPoint::TMP0)) {
break;
}
std::list<PointIndex> boundary;
SplitBoundaryFromOpenEdges(openEdges, boundary);
rBorders.emplace_back(boundary.begin(), boundary.end());
}
}
void MeshAlgorithm::SplitBoundaryFromOpenEdges(
std::list<std::pair<PointIndex, PointIndex>>& openEdges,
std::list<PointIndex>& boundary
) const
{
// Start with the edge that is associated to uFacet
if (openEdges.empty()) {
return;
}
PointIndex ulFirst = openEdges.begin()->first;
PointIndex ulLast = openEdges.begin()->second;
openEdges.erase(openEdges.begin());
boundary.push_back(ulFirst);
boundary.push_back(ulLast);
while (ulLast != ulFirst) {
// find adjacent edge
std::list<std::pair<PointIndex, PointIndex>>::iterator pEI;
for (pEI = openEdges.begin(); pEI != openEdges.end(); ++pEI) {
if (pEI->first == ulLast) {
ulLast = pEI->second;
boundary.push_back(ulLast);
openEdges.erase(pEI);
pEI = openEdges.begin();
break;
}
if (pEI->second == ulFirst) {
ulFirst = pEI->first;
boundary.push_front(ulFirst);
openEdges.erase(pEI);
pEI = openEdges.begin();
break;
}
}
// cannot close the border
if (pEI == openEdges.end()) {
break;
}
}
}
void MeshAlgorithm::SplitBoundaryLoops(std::list<std::vector<PointIndex>>& aBorders)
{
// Count the number of open edges for each point
std::map<PointIndex, int> openPointDegree;
for (const auto& jt : _rclMesh._aclFacetArray) {
for (int i = 0; i < 3; i++) {
if (jt._aulNeighbours[i] == FACET_INDEX_MAX) {
openPointDegree[jt._aulPoints[i]]++;
openPointDegree[jt._aulPoints[(i + 1) % 3]]++;
}
}
}
// go through all boundaries and split them if needed
std::list<std::vector<PointIndex>> aSplitBorders;
for (const auto& aBorder : aBorders) {
bool split = false;
for (auto jt : aBorder) {
// two (or more) boundaries meet in one non-manifold point
if (openPointDegree[jt] > 2) {
split = true;
break;
}
}
if (!split) {
aSplitBorders.push_back(aBorder);
}
else {
SplitBoundaryLoops(aBorder, aSplitBorders);
}
}
aBorders = aSplitBorders;
}
void MeshAlgorithm::SplitBoundaryLoops(
const std::vector<PointIndex>& rBound,
std::list<std::vector<PointIndex>>& aBorders
)
{
std::map<PointIndex, int> aPtDegree;
std::vector<PointIndex> cBound;
for (PointIndex it : rBound) {
int deg = (aPtDegree[it]++);
if (deg > 0) {
for (auto jt = cBound.begin(); jt != cBound.end(); ++jt) {
if (*jt == it) {
std::vector<PointIndex> cBoundLoop;
cBoundLoop.insert(cBoundLoop.end(), jt, cBound.end());
cBoundLoop.push_back(it);
cBound.erase(jt, cBound.end());
aBorders.push_back(cBoundLoop);
(aPtDegree[it]--);
break;
}
}
}
cBound.push_back(it);
}
}
bool MeshAlgorithm::FillupHole(
const std::vector<PointIndex>& boundary,
AbstractPolygonTriangulator& cTria,
MeshFacetArray& rFaces,
MeshPointArray& rPoints,
int level,
const MeshRefPointToFacets* pP2FStructure
) const
{
if (boundary.front() == boundary.back()) {
// first and last vertex are identical
if (boundary.size() < 4) {
return false; // something strange
}
}
else if (boundary.size() < 3) {
return false; // something strange
}
// Get a facet as reference coordinate system
MeshGeomFacet rTriangle;
MeshFacet rFace;
PointIndex refPoint0 = *(boundary.begin());
PointIndex refPoint1 = *(boundary.begin() + 1);
if (pP2FStructure) {
const std::set<FacetIndex>& ring1 = (*pP2FStructure)[refPoint0];
const std::set<FacetIndex>& ring2 = (*pP2FStructure)[refPoint1];
std::vector<FacetIndex> f_int;
std::set_intersection(
ring1.begin(),
ring1.end(),
ring2.begin(),
ring2.end(),
std::back_insert_iterator<std::vector<FacetIndex>>(f_int)
);
if (f_int.size() != 1) {
return false; // error, this must be an open edge!
}
rFace = _rclMesh._aclFacetArray[f_int.front()];
rTriangle = _rclMesh.GetFacet(rFace);
}
else {
bool ready = false;
for (auto it = _rclMesh._aclFacetArray.begin(); it != _rclMesh._aclFacetArray.end(); ++it) {
for (int i = 0; i < 3; i++) {
if (((it->_aulPoints[i] == refPoint0) && (it->_aulPoints[(i + 1) % 3] == refPoint1))
|| ((it->_aulPoints[i] == refPoint1)
&& (it->_aulPoints[(i + 1) % 3] == refPoint0))) {
rFace = *it;
rTriangle = _rclMesh.GetFacet(*it);
ready = true;
break;
}
}
if (ready) {
break;
}
}
}
// add points to the polygon
std::vector<Base::Vector3f> polygon;
for (PointIndex jt : boundary) {
polygon.push_back(_rclMesh._aclPointArray[jt]);
rPoints.push_back(_rclMesh._aclPointArray[jt]);
}
// remove the last added point if it is duplicated
std::vector<PointIndex> bounds = boundary;
if (boundary.front() == boundary.back()) {
bounds.pop_back();
polygon.pop_back();
rPoints.pop_back();
}
// There is no easy way to check whether the boundary is interior (a hole) or exterior before
// performing the triangulation. Afterwards we can compare the normals of the created triangles
// with the z-direction of our local coordinate system. If the scalar product is positive it was
// a hole, otherwise not.
cTria.SetPolygon(polygon);
cTria.SetIndices(bounds);
std::vector<Base::Vector3f> surf_pts = cTria.GetPolygon();
if (pP2FStructure && level > 0) {
std::set<PointIndex> index = pP2FStructure->NeighbourPoints(boundary, level);
for (PointIndex it : index) {
Base::Vector3f pt(_rclMesh._aclPointArray[it]);
surf_pts.push_back(pt);
}
}
if (cTria.TriangulatePolygon()) {
// if we have enough points then we fit a surface through the points and project
// the added points onto this surface
cTria.PostProcessing(surf_pts);
// get the facets and add the additional points to the array
rFaces.insert(rFaces.end(), cTria.GetFacets().begin(), cTria.GetFacets().end());
std::vector<Base::Vector3f> newVertices = cTria.AddedPoints();
for (const auto& vertex : newVertices) {
rPoints.push_back(vertex);
}
// Unfortunately, some algorithms do not care about the orientation of the polygon so we
// cannot rely on the normal criterion to decide whether it's a hole or not.
//
std::vector<MeshFacet> faces = cTria.GetFacets();
// Special case handling for a hole with three edges: the resulting facet might be
// coincident with the reference facet
if (faces.size() == 1) {
MeshFacet first = faces.front();
if (cTria.NeedsReindexing()) {
first._aulPoints[0] = boundary[first._aulPoints[0]];
first._aulPoints[1] = boundary[first._aulPoints[1]];
first._aulPoints[2] = boundary[first._aulPoints[2]];
}
if (first.IsEqual(rFace)) {
rFaces.clear();
rPoints.clear();
cTria.Discard();
return false;
}
}
constexpr auto max = std::numeric_limits<unsigned short>::max();
// Get the new neighbour to our reference facet
MeshFacet facet;
unsigned short ref_side = rFace.Side(refPoint0, refPoint1);
unsigned short tri_side = max;
if (cTria.NeedsReindexing()) {
// the referenced indices of the polyline
refPoint0 = 0;
refPoint1 = 1;
}
if (ref_side < max) {
for (const auto& face : faces) {
tri_side = face.Side(refPoint0, refPoint1);
if (tri_side < max) {
facet = face;
break;
}
}
}
// in case the reference facet has not an open edge print a log message
if (ref_side == max || tri_side == max) {
Base::Console().log(
"MeshAlgorithm::FillupHole: Expected open edge for facet <%d, %d, %d>\n",
rFace._aulPoints[0],
rFace._aulPoints[1],
rFace._aulPoints[2]
);
rFaces.clear();
rPoints.clear();
cTria.Discard();
return false;
}
#if 1
MeshGeomFacet triangle;
triangle = cTria.GetTriangle(rPoints, facet);
TriangulationVerifier* verifier = cTria.GetVerifier();
if (!verifier) {
return true;
}
// Now we have two adjacent triangles which we check for overlaps.
// Therefore we build a separation plane that must separate the two diametrically opposed
// points.
Base::Vector3f planeNormal = rTriangle.GetNormal()
% (rTriangle._aclPoints[(ref_side + 1) % 3] - rTriangle._aclPoints[ref_side]);
Base::Vector3f planeBase = rTriangle._aclPoints[ref_side % 3];
Base::Vector3f ref_point = rTriangle._aclPoints[(ref_side + 2) % 3];
Base::Vector3f tri_point = triangle._aclPoints[(tri_side + 2) % 3];
if (!verifier->Accept(planeNormal, planeBase, ref_point, tri_point)) {
rFaces.clear();
rPoints.clear();
cTria.Discard();
return false;
}
// we know to have filled a polygon, now check for the orientation
if (verifier->MustFlip(triangle.GetNormal(), rTriangle.GetNormal())) {
for (auto& rFace : rFaces) {
rFace.FlipNormal();
}
}
#endif
return true;
}
return false;
}
void MeshAlgorithm::SetFacetsProperty(
const std::vector<FacetIndex>& raulInds,
const std::vector<unsigned long>& raulProps
) const
{
if (raulInds.size() != raulProps.size()) {
return;
}
auto iP = raulProps.begin();
for (auto i = raulInds.begin(); i != raulInds.end(); ++i, ++iP) {
_rclMesh._aclFacetArray[*i].SetProperty(*iP);
}
}
void MeshAlgorithm::SetFacetsFlag(const std::vector<FacetIndex>& raulInds, MeshFacet::TFlagType tF) const
{
for (FacetIndex it : raulInds) {
_rclMesh._aclFacetArray[it].SetFlag(tF);
}
}
void MeshAlgorithm::SetPointsFlag(const std::vector<FacetIndex>& raulInds, MeshPoint::TFlagType tF) const
{
for (PointIndex it : raulInds) {
_rclMesh._aclPointArray[it].SetFlag(tF);
}
}
void MeshAlgorithm::GetFacetsFlag(std::vector<FacetIndex>& raulInds, MeshFacet::TFlagType tF) const
{
raulInds.reserve(raulInds.size() + CountFacetFlag(tF));
MeshFacetArray::_TConstIterator beg = _rclMesh._aclFacetArray.begin();
MeshFacetArray::_TConstIterator end = _rclMesh._aclFacetArray.end();
for (auto it = beg; it != end; ++it) {
if (it->IsFlag(tF)) {
raulInds.push_back(it - beg);
}
}
}
void MeshAlgorithm::GetPointsFlag(std::vector<PointIndex>& raulInds, MeshPoint::TFlagType tF) const
{
raulInds.reserve(raulInds.size() + CountPointFlag(tF));
MeshPointArray::_TConstIterator beg = _rclMesh._aclPointArray.begin();
MeshPointArray::_TConstIterator end = _rclMesh._aclPointArray.end();
for (auto it = beg; it != end; ++it) {
if (it->IsFlag(tF)) {
raulInds.push_back(it - beg);
}
}
}
void MeshAlgorithm::ResetFacetsFlag(const std::vector<FacetIndex>& raulInds, MeshFacet::TFlagType tF) const
{
for (FacetIndex it : raulInds) {
_rclMesh._aclFacetArray[it].ResetFlag(tF);
}
}
void MeshAlgorithm::ResetPointsFlag(const std::vector<FacetIndex>& raulInds, MeshPoint::TFlagType tF) const
{
for (PointIndex it : raulInds) {
_rclMesh._aclPointArray[it].ResetFlag(tF);
}
}
void MeshAlgorithm::SetFacetFlag(MeshFacet::TFlagType tF) const
{
_rclMesh._aclFacetArray.SetFlag(tF);
}
void MeshAlgorithm::SetPointFlag(MeshPoint::TFlagType tF) const
{
_rclMesh._aclPointArray.SetFlag(tF);
}
void MeshAlgorithm::ResetFacetFlag(MeshFacet::TFlagType tF) const
{
_rclMesh._aclFacetArray.ResetFlag(tF);
}
void MeshAlgorithm::ResetPointFlag(MeshPoint::TFlagType tF) const
{
_rclMesh._aclPointArray.ResetFlag(tF);
}
unsigned long MeshAlgorithm::CountFacetFlag(MeshFacet::TFlagType tF) const
{
MeshIsFlag<MeshFacet> flag;
return std::count_if(
_rclMesh._aclFacetArray.begin(),
_rclMesh._aclFacetArray.end(),
[flag, tF](const MeshFacet& f) { return flag(f, tF); }
);
}
unsigned long MeshAlgorithm::CountPointFlag(MeshPoint::TFlagType tF) const
{
MeshIsFlag<MeshPoint> flag;
return std::count_if(
_rclMesh._aclPointArray.begin(),
_rclMesh._aclPointArray.end(),
[flag, tF](const MeshPoint& f) { return flag(f, tF); }
);
}
void MeshAlgorithm::GetFacetsFromToolMesh(
const MeshKernel& rToolMesh,
const Base::Vector3f& rcDir,
std::vector<FacetIndex>& raclCutted
) const
{
MeshFacetIterator cFIt(_rclMesh);
MeshFacetIterator cTIt(rToolMesh);
BoundBox3f cBB = rToolMesh.GetBoundBox();
Base::SequencerLauncher seq("Check facets...", _rclMesh.CountFacets());
// check all facets
Base::Vector3f tmp;
for (cFIt.Init(); cFIt.More(); cFIt.Next()) {
// check each point of each facet
for (const auto& pnt : cFIt->_aclPoints) {
// at least the point must be inside the bounding box of the tool mesh
if (cBB.IsInBox(pnt)) {
// should not cause performance problems since the tool mesh is usually rather
// lightweight
int ct = 0;
for (cTIt.Init(); cTIt.More(); cTIt.Next()) {
if (cTIt->IsPointOfFace(pnt, MeshPoint::epsilon())) {
ct = 1;
break; // the point lies on the tool mesh
}
if (cTIt->Foraminate(pnt, rcDir, tmp)) {
// check if the intersection point lies in direction rcDir of the considered
// point
if ((tmp - pnt) * rcDir > 0) {
ct++;
}
}
}
// odd number => point is inside the tool mesh
if (ct % 2 == 1) {
raclCutted.push_back(cFIt.Position());
break;
}
}
}
seq.next();
}
}
void MeshAlgorithm::GetFacetsFromToolMesh(
const MeshKernel& rToolMesh,
const Base::Vector3f& rcDir,
const MeshFacetGrid& rGrid,
std::vector<FacetIndex>& raclCutted
) const
{
// iterator over grid structure
MeshGridIterator clGridIter(rGrid);
BoundBox3f cBB = rToolMesh.GetBoundBox();
Base::Vector3f tmp;
MeshFacetIterator cFIt(_rclMesh);
MeshFacetIterator cTIt(rToolMesh);
MeshAlgorithm cToolAlg(rToolMesh);
// To speed up the algorithm we use the grid built up from the associated mesh. For each grid
// element we check whether it lies completely inside or outside the toolmesh or even intersects
// with the toolmesh. So we can reduce the number of facets with further tests dramatically.
// If the grid box is outside the toolmesh all the facets inside can be skipped. If the grid
// box is inside the toolmesh all facets are stored with no further tests because they must
// also lie inside the toolmesh. Finally, if the grid box intersects with the toolmesh we must
// also check for each whether it intersects with the toolmesh as well.
std::vector<FacetIndex> aulInds;
for (clGridIter.Init(); clGridIter.More(); clGridIter.Next()) {
int ret = cToolAlg.Surround(clGridIter.GetBoundBox(), rcDir);
// the box is completely inside the toolmesh
if (ret == 1) {
// these facets can be removed without more checks
clGridIter.GetElements(raclCutted);
}
// the box intersects with toolmesh
else if (ret == 0) {
// these facets must be tested for intersections with the toolmesh
clGridIter.GetElements(aulInds);
}
// the box is outside the toolmesh but this could still mean that the triangles
// inside the grid intersect with the toolmesh
else if (ret == -1) {
// these facets must be tested for intersections with the toolmesh
clGridIter.GetElements(aulInds);
}
}
// remove duplicates
std::sort(aulInds.begin(), aulInds.end());
aulInds.erase(std::unique(aulInds.begin(), aulInds.end()), aulInds.end());
std::sort(raclCutted.begin(), raclCutted.end());
raclCutted.erase(std::unique(raclCutted.begin(), raclCutted.end()), raclCutted.end());
Base::SequencerLauncher seq("Check facets...", aulInds.size());
// check all facets
for (FacetIndex it : aulInds) {
cFIt.Set(it);
// check each point of each facet
for (auto point : cFIt->_aclPoints) {
// at least the point must be inside the bounding box of the tool mesh
if (cBB.IsInBox(point)) {
// should not cause performance problems since the tool mesh is usually rather
// lightweight
int ct = 0;
for (cTIt.Init(); cTIt.More(); cTIt.Next()) {
if (cTIt->IsPointOfFace(point, MeshPoint::epsilon())) {
ct = 1;
break; // the point lies on the tool mesh
}
if (cTIt->Foraminate(point, rcDir, tmp)) {
// check if the intersection point lies in direction rcDir of the considered
// point
if ((tmp - point) * rcDir > 0) {
ct++;
}
}
}
// odd number => point is inside the tool mesh
if (ct % 2 == 1) {
raclCutted.push_back(cFIt.Position());
break;
}
}
}
seq.next();
}
// remove duplicates
std::sort(raclCutted.begin(), raclCutted.end());
raclCutted.erase(std::unique(raclCutted.begin(), raclCutted.end()), raclCutted.end());
}
int MeshAlgorithm::Surround(const Base::BoundBox3f& rBox, const Base::Vector3f& rcDir)
{
Base::Vector3f pt1, pt2, tmp;
const BoundBox3f& cBB = _rclMesh.GetBoundBox();
// at least both boxes intersect
if (cBB && rBox) {
// check for intersections with the actual mesh
Base::Vector3f cCorner[8] = {
Base::Vector3f(rBox.MinX, rBox.MinY, rBox.MinZ),
Base::Vector3f(rBox.MaxX, rBox.MinY, rBox.MinZ),
Base::Vector3f(rBox.MaxX, rBox.MaxY, rBox.MinZ),
Base::Vector3f(rBox.MinX, rBox.MaxY, rBox.MinZ),
Base::Vector3f(rBox.MinX, rBox.MinY, rBox.MaxZ),
Base::Vector3f(rBox.MaxX, rBox.MinY, rBox.MaxZ),
Base::Vector3f(rBox.MaxX, rBox.MaxY, rBox.MaxZ),
Base::Vector3f(rBox.MinX, rBox.MaxY, rBox.MaxZ)
};
MeshFacetIterator cTIt(_rclMesh);
// triangulation of the box
int triangles[36] = {0, 1, 2, 0, 2, 3, 0, 1, 5, 0, 5, 4, 0, 4, 7, 0, 7, 3,
6, 7, 4, 6, 4, 5, 6, 2, 3, 6, 3, 7, 6, 1, 2, 6, 5, 1};
std::vector<MeshGeomFacet> cFacet(12);
int id = 0;
for (size_t ii = 0; ii < 12; ii++) {
cFacet[ii]._aclPoints[0] = cCorner[triangles[id++]];
cFacet[ii]._aclPoints[1] = cCorner[triangles[id++]];
cFacet[ii]._aclPoints[2] = cCorner[triangles[id++]];
}
// check for intersections of the box with the mesh
for (const auto& it : cFacet) {
for (cTIt.Init(); cTIt.More(); cTIt.Next()) {
int ret = cTIt->IntersectWithFacet(it, pt1, pt2);
// the box intersects the mesh?
if (ret != 0) {
return 0; // => no more investigations required
}
}
}
// Now we know that the box doesn't intersect with the mesh. This means that either the box
// is completely inside or outside the mesh. To check this we test one point of the box
// whether it is inside or outside.
int ct = 0;
for (cTIt.Init(); cTIt.More(); cTIt.Next()) {
if (cTIt->IsPointOfFace(cCorner[0], MeshPoint::epsilon())) {
ct = 1;
break; // the point lies on the tool mesh
}
if (cTIt->Foraminate(cCorner[0], rcDir, tmp)) {
// check if the intersection point lies in direction rcDir of the considered point
if ((tmp - cCorner[0]) * rcDir > 0) {
ct++;
}
}
}
// odd number => point (i.e. the box) is inside the mesh, even number => point is outside
// the mesh
return (ct % 2 == 1) ? 1 : -1;
}
// no intersection the box is outside the mesh
return -1;
}
void MeshAlgorithm::CheckFacets(
const MeshFacetGrid& rclGrid,
const Base::ViewProjMethod* pclProj,
const Base::Polygon2d& rclPoly,
bool bInner,
std::vector<FacetIndex>& raulFacets
) const
{
std::vector<FacetIndex>::iterator it;
MeshFacetIterator clIter(_rclMesh, 0);
Base::Vector3f clPt2d;
Base::Vector3f clGravityOfFacet;
bool bNoPointInside {};
// Cache current view projection matrix since calls to Coin's projection are expensive
Base::ViewProjMatrix fixedProj(pclProj->getComposedProjectionMatrix());
// Precompute the polygon's bounding box
Base::BoundBox2d clPolyBBox = rclPoly.CalcBoundBox();
// if true use grid on mesh to speed up search
if (bInner) {
BoundBox3f clBBox3d;
BoundBox2d clViewBBox;
std::vector<FacetIndex> aulAllElements;
// iterator for the bounding box grids
MeshGridIterator clGridIter(rclGrid);
for (clGridIter.Init(); clGridIter.More(); clGridIter.Next()) {
clBBox3d = clGridIter.GetBoundBox();
clViewBBox = clBBox3d.ProjectBox(&fixedProj);
if (clViewBBox.Intersect(clPolyBBox)) {
// collect all elements in aulAllElements
clGridIter.GetElements(aulAllElements);
}
}
// remove duplicates
std::sort(aulAllElements.begin(), aulAllElements.end());
aulAllElements.erase(
std::unique(aulAllElements.begin(), aulAllElements.end()),
aulAllElements.end()
);
Base::SequencerLauncher seq("Check facets", aulAllElements.size());
for (it = aulAllElements.begin(); it != aulAllElements.end(); ++it) {
bNoPointInside = true;
clGravityOfFacet.Set(0.0F, 0.0F, 0.0F);
MeshGeomFacet rclFacet = _rclMesh.GetFacet(*it);
for (const auto& pnt : rclFacet._aclPoints) {
clPt2d = fixedProj(pnt);
clGravityOfFacet += clPt2d;
if (clPolyBBox.Contains(Base::Vector2d(clPt2d.x, clPt2d.y))
&& rclPoly.Contains(Base::Vector2d(clPt2d.x, clPt2d.y))) {
raulFacets.push_back(*it);
bNoPointInside = false;
break;
}
}
// if no facet point is inside the polygon then check also the gravity
if (bNoPointInside) {
clGravityOfFacet *= 1.0F / 3.0F;
if (clPolyBBox.Contains(Base::Vector2d(clGravityOfFacet.x, clGravityOfFacet.y))
&& rclPoly.Contains(Base::Vector2d(clGravityOfFacet.x, clGravityOfFacet.y))) {
raulFacets.push_back(*it);
}
}
seq.next();
}
}
// When cutting triangles outside then go through all elements
else {
Base::SequencerLauncher seq("Check facets", _rclMesh.CountFacets());
for (clIter.Init(); clIter.More(); clIter.Next()) {
for (const auto& pnt : clIter->_aclPoints) {
clPt2d = fixedProj(pnt);
if ((clPolyBBox.Contains(Base::Vector2d(clPt2d.x, clPt2d.y))
&& !rclPoly.Contains(Base::Vector2d(clPt2d.x, clPt2d.y)))) {
raulFacets.push_back(clIter.Position());
break;
}
}
seq.next();
}
}
}
void MeshAlgorithm::CheckFacets(
const Base::ViewProjMethod* pclProj,
const Base::Polygon2d& rclPoly,
bool bInner,
std::vector<FacetIndex>& raulFacets
) const
{
const MeshPointArray& p = _rclMesh.GetPoints();
const MeshFacetArray& f = _rclMesh.GetFacets();
Base::Vector3f pt2d;
// Use a bounding box to reduce number of call to Polygon::Contains
Base::BoundBox2d bb = rclPoly.CalcBoundBox();
// Precompute the screen projection matrix as Coin's projection function is expensive
Base::ViewProjMatrix fixedProj(pclProj->getComposedProjectionMatrix());
FacetIndex index = 0;
for (auto it = f.begin(); it != f.end(); ++it, ++index) {
for (PointIndex ptIndex : it->_aulPoints) {
pt2d = fixedProj(p[ptIndex]);
// First check whether the point is in the bounding box of the polygon
if ((bb.Contains(Base::Vector2d(pt2d.x, pt2d.y))
&& rclPoly.Contains(Base::Vector2d(pt2d.x, pt2d.y)))
^ !bInner) {
raulFacets.push_back(index);
break;
}
}
}
}
float MeshAlgorithm::Surface() const
{
float fTotal = 0.0F;
MeshFacetIterator clFIter(_rclMesh);
for (clFIter.Init(); clFIter.More(); clFIter.Next()) {
fTotal += clFIter->Area();
}
return fTotal;
}
void MeshAlgorithm::SubSampleByDist(float fDist, std::vector<Base::Vector3f>& rclPoints) const
{
rclPoints.clear();
MeshFacetIterator clFIter(_rclMesh);
for (clFIter.Init(); clFIter.More(); clFIter.Next()) {
size_t k = rclPoints.size();
clFIter->SubSample(fDist, rclPoints);
if (rclPoints.size() == k) {
rclPoints.push_back(clFIter->GetGravityPoint()); // min. add middle point
}
}
}
void MeshAlgorithm::SubSampleAllPoints(std::vector<Base::Vector3f>& rclPoints) const
{
rclPoints.clear();
// Add all Points
//
MeshPointIterator clPIter(_rclMesh);
for (clPIter.Init(); clPIter.More(); clPIter.Next()) {
rclPoints.push_back(*clPIter);
}
}
void MeshAlgorithm::SubSampleByCount(unsigned long ulCtPoints, std::vector<Base::Vector3f>& rclPoints) const
{
float fDist = float(std::sqrt(Surface() / float(ulCtPoints)));
SubSampleByDist(fDist, rclPoints);
}
void MeshAlgorithm::SearchFacetsFromPolyline(
const std::vector<Base::Vector3f>& rclPolyline,
float fRadius,
const MeshFacetGrid& rclGrid,
std::vector<FacetIndex>& rclResultFacetsIndices
) const
{
rclResultFacetsIndices.clear();
if (rclPolyline.size() < 3) {
return; // no polygon defined
}
std::set<FacetIndex> aclFacets;
for (auto pV = rclPolyline.begin(); pV < (rclPolyline.end() - 1); ++pV) {
const Base::Vector3f &rclP0 = *pV, &rclP1 = *(pV + 1);
// BB eines Polyline-Segments
BoundBox3f clSegmBB(rclP0.x, rclP0.y, rclP0.z, rclP0.x, rclP0.y, rclP0.z);
clSegmBB.Add(rclP1);
clSegmBB.Enlarge(fRadius); // BB um Suchradius vergroessern
std::vector<FacetIndex> aclBBFacets;
unsigned long k = rclGrid.Inside(clSegmBB, aclBBFacets, false);
for (unsigned long i = 0; i < k; i++) {
if (_rclMesh.GetFacet(aclBBFacets[i]).DistanceToLineSegment(rclP0, rclP1) < fRadius) {
aclFacets.insert(aclBBFacets[i]);
}
}
}
rclResultFacetsIndices.insert(rclResultFacetsIndices.begin(), aclFacets.begin(), aclFacets.end());
}
void MeshAlgorithm::CutBorderFacets(std::vector<FacetIndex>& raclFacetIndices, unsigned short usLevel) const
{
std::vector<FacetIndex> aclToDelete;
CheckBorderFacets(raclFacetIndices, aclToDelete, usLevel);
// alle gefunden "Rand"-Facetsindizes" aus dem Array loeschen
std::vector<FacetIndex> aclResult;
std::set<FacetIndex> aclTmp(aclToDelete.begin(), aclToDelete.end());
for (FacetIndex facetIndex : raclFacetIndices) {
if (aclTmp.find(facetIndex) == aclTmp.end()) {
aclResult.push_back(facetIndex);
}
}
raclFacetIndices = aclResult;
}
unsigned long MeshAlgorithm::CountBorderEdges() const
{
unsigned long cnt = 0;
const MeshFacetArray& rclFAry = _rclMesh._aclFacetArray;
auto end = rclFAry.end();
for (auto it = rclFAry.begin(); it != end; ++it) {
for (FacetIndex facetIndex : it->_aulNeighbours) {
if (facetIndex == FACET_INDEX_MAX) {
cnt++;
}
}
}
return cnt;
}
void MeshAlgorithm::CheckBorderFacets(
const std::vector<FacetIndex>& raclFacetIndices,
std::vector<FacetIndex>& raclResultIndices,
unsigned short usLevel
) const
{
ResetFacetFlag(MeshFacet::TMP0);
SetFacetsFlag(raclFacetIndices, MeshFacet::TMP0);
const MeshFacetArray& rclFAry = _rclMesh._aclFacetArray;
for (unsigned short usL = 0; usL < usLevel; usL++) {
for (FacetIndex facetIndex : raclFacetIndices) {
for (FacetIndex ulNB : rclFAry[facetIndex]._aulNeighbours) {
if (ulNB == FACET_INDEX_MAX) {
raclResultIndices.push_back(facetIndex);
rclFAry[facetIndex].ResetFlag(MeshFacet::TMP0);
continue;
}
if (!rclFAry[ulNB].IsFlag(MeshFacet::TMP0)) {
raclResultIndices.push_back(facetIndex);
rclFAry[facetIndex].ResetFlag(MeshFacet::TMP0);
continue;
}
}
}
}
}
void MeshAlgorithm::GetBorderPoints(
const std::vector<FacetIndex>& raclFacetIndices,
std::set<PointIndex>& raclResultPointsIndices
) const
{
ResetFacetFlag(MeshFacet::TMP0);
SetFacetsFlag(raclFacetIndices, MeshFacet::TMP0);
const MeshFacetArray& rclFAry = _rclMesh._aclFacetArray;
for (FacetIndex facetIndex : raclFacetIndices) {
for (int i = 0; i < 3; i++) {
const MeshFacet& rclFacet = rclFAry[facetIndex];
FacetIndex ulNB = rclFacet._aulNeighbours[i];
if (ulNB == FACET_INDEX_MAX) {
raclResultPointsIndices.insert(rclFacet._aulPoints[i]);
raclResultPointsIndices.insert(rclFacet._aulPoints[(i + 1) % 3]);
continue;
}
if (!rclFAry[ulNB].IsFlag(MeshFacet::TMP0)) {
raclResultPointsIndices.insert(rclFacet._aulPoints[i]);
raclResultPointsIndices.insert(rclFacet._aulPoints[(i + 1) % 3]);
continue;
}
}
}
}
bool MeshAlgorithm::NearestPointFromPoint(
const Base::Vector3f& rclPt,
FacetIndex& rclResFacetIndex,
Base::Vector3f& rclResPoint
) const
{
if (_rclMesh.CountFacets() == 0) {
return false;
}
// calc each facet
float fMinDist = std::numeric_limits<float>::max();
FacetIndex ulInd = FACET_INDEX_MAX;
MeshFacetIterator pF(_rclMesh);
for (pF.Init(); pF.More(); pF.Next()) {
float fDist = pF->DistanceToPoint(rclPt);
if (fDist < fMinDist) {
fMinDist = fDist;
ulInd = pF.Position();
}
}
MeshGeomFacet rclSFacet = _rclMesh.GetFacet(ulInd);
rclSFacet.DistanceToPoint(rclPt, rclResPoint);
rclResFacetIndex = ulInd;
return true;
}
bool MeshAlgorithm::NearestPointFromPoint(
const Base::Vector3f& rclPt,
const MeshFacetGrid& rclGrid,
FacetIndex& rclResFacetIndex,
Base::Vector3f& rclResPoint
) const
{
FacetIndex ulInd = rclGrid.SearchNearestFromPoint(rclPt);
if (ulInd == FACET_INDEX_MAX) {
return false;
}
MeshGeomFacet rclSFacet = _rclMesh.GetFacet(ulInd);
rclSFacet.DistanceToPoint(rclPt, rclResPoint);
rclResFacetIndex = ulInd;
return true;
}
bool MeshAlgorithm::NearestPointFromPoint(
const Base::Vector3f& rclPt,
const MeshFacetGrid& rclGrid,
float fMaxSearchArea,
FacetIndex& rclResFacetIndex,
Base::Vector3f& rclResPoint
) const
{
FacetIndex ulInd = rclGrid.SearchNearestFromPoint(rclPt, fMaxSearchArea);
if (ulInd == FACET_INDEX_MAX) {
return false; // no facets inside BoundingBox
}
MeshGeomFacet rclSFacet = _rclMesh.GetFacet(ulInd);
rclSFacet.DistanceToPoint(rclPt, rclResPoint);
rclResFacetIndex = ulInd;
return true;
}
bool MeshAlgorithm::CutWithPlane(
const Base::Vector3f& clBase,
const Base::Vector3f& clNormal,
const MeshFacetGrid& rclGrid,
std::list<std::vector<Base::Vector3f>>& rclResult,
float fMinEps,
bool bConnectPolygons
) const
{
std::vector<FacetIndex> aulFacets;
// Search grid
MeshGridIterator clGridIter(rclGrid);
for (clGridIter.Init(); clGridIter.More(); clGridIter.Next()) {
// if Gridvoxel intersects the plane: pick up all facets for cutting
if (clGridIter.GetBoundBox().IsCutPlane(clBase, clNormal)) {
clGridIter.GetElements(aulFacets);
}
}
// remove multiple triangles
std::sort(aulFacets.begin(), aulFacets.end());
aulFacets.erase(std::unique(aulFacets.begin(), aulFacets.end()), aulFacets.end());
// intersect all facets with plane
std::list<std::pair<Base::Vector3f, Base::Vector3f>> clTempPoly; // Field with intersection lines
// (unsorted, not chained)
for (FacetIndex facetIndex : aulFacets) {
Base::Vector3f clE1, clE2;
const MeshGeomFacet clF(_rclMesh.GetFacet(facetIndex));
// Cut the facet and store the cutting path
if (clF.IntersectWithPlane(clBase, clNormal, clE1, clE2)) {
clTempPoly.emplace_back(clE1, clE2);
}
}
if (bConnectPolygons) {
// std::list<std::pair<Base::Vector3f, Base::Vector3f> > rclTempLines;
std::list<std::pair<Base::Vector3f, Base::Vector3f>> rclResultLines(
clTempPoly.begin(),
clTempPoly.end()
);
std::list<std::vector<Base::Vector3f>> tempList;
ConnectLines(clTempPoly, tempList, fMinEps);
ConnectPolygons(tempList, clTempPoly);
for (auto& iter : clTempPoly) {
rclResultLines.push_front(iter);
}
return ConnectLines(rclResultLines, rclResult, fMinEps);
}
return ConnectLines(clTempPoly, rclResult, fMinEps);
}
bool MeshAlgorithm::ConnectLines(
std::list<std::pair<Base::Vector3f, Base::Vector3f>>& rclLines,
std::list<std::vector<Base::Vector3f>>& rclPolylines,
float fMinEps
) const
{
using TCIter = std::list<std::pair<Base::Vector3f, Base::Vector3f>>::iterator;
// square search radius
// const float fMinEps = 1.0e-2f; // := 10 micrometer distance
fMinEps = fMinEps * fMinEps;
// remove all lines whose distance is smaller than epsilon
std::list<TCIter> _clToDelete;
float fToDelDist = fMinEps / 10.0F;
for (TCIter pF = rclLines.begin(); pF != rclLines.end(); ++pF) {
if (Base::DistanceP2(pF->first, pF->second) < fToDelDist) {
_clToDelete.push_back(pF);
}
}
for (auto& pI : _clToDelete) {
rclLines.erase(pI);
}
while (!rclLines.empty()) {
TCIter pF;
// new polyline
std::list<Base::Vector3f> clPoly;
// add first line and delete from the list
Base::Vector3f clFront = rclLines.begin()->first; // current start point of the polyline
Base::Vector3f clEnd = rclLines.begin()->second; // current end point of the polyline
clPoly.push_back(clFront);
clPoly.push_back(clEnd);
rclLines.erase(rclLines.begin());
// search for the next line on the begin/end of the polyline and add it
TCIter pFront, pEnd;
bool bFoundLine {};
do {
float fFrontMin = fMinEps, fEndMin = fMinEps;
bool bFrontFirst = false, bEndFirst = false;
pFront = rclLines.end();
pEnd = rclLines.end();
bFoundLine = false;
for (pF = rclLines.begin(); pF != rclLines.end(); ++pF) {
if (Base::DistanceP2(clFront, pF->first) < fFrontMin) {
fFrontMin = Base::DistanceP2(clFront, pF->first);
pFront = pF;
bFrontFirst = true;
}
else if (Base::DistanceP2(clEnd, pF->first) < fEndMin) {
fEndMin = Base::DistanceP2(clEnd, pF->first);
pEnd = pF;
bEndFirst = true;
}
else if (Base::DistanceP2(clFront, pF->second) < fFrontMin) {
fFrontMin = Base::DistanceP2(clFront, pF->second);
pFront = pF;
bFrontFirst = false;
}
else if (Base::DistanceP2(clEnd, pF->second) < fEndMin) {
fEndMin = Base::DistanceP2(clEnd, pF->second);
pEnd = pF;
bEndFirst = false;
}
}
if (pFront != rclLines.end()) {
bFoundLine = true;
if (bFrontFirst) {
clPoly.push_front(pFront->second);
clFront = pFront->second;
}
else {
clPoly.push_front(pFront->first);
clFront = pFront->first;
}
rclLines.erase(pFront);
}
if (pEnd != rclLines.end()) {
bFoundLine = true;
if (bEndFirst) {
clPoly.push_back(pEnd->second);
clEnd = pEnd->second;
}
else {
clPoly.push_back(pEnd->first);
clEnd = pEnd->first;
}
rclLines.erase(pEnd);
}
} while (bFoundLine);
rclPolylines.emplace_back(clPoly.begin(), clPoly.end());
}
// remove all polylines with too few length
using TPIter = std::list<std::vector<Base::Vector3f>>::iterator;
std::list<TPIter> _clPolyToDelete;
for (TPIter pJ = rclPolylines.begin(); pJ != rclPolylines.end(); ++pJ) {
if (pJ->size() == 2) { // only one line segment
if (Base::DistanceP2(*pJ->begin(), *(pJ->begin() + 1)) <= fMinEps) {
_clPolyToDelete.push_back(pJ);
}
}
}
for (auto& pK : _clPolyToDelete) {
rclPolylines.erase(pK);
}
return true;
}
bool MeshAlgorithm::ConnectPolygons(
std::list<std::vector<Base::Vector3f>>& clPolyList,
std::list<std::pair<Base::Vector3f, Base::Vector3f>>& rclLines
) const
{
for (auto OutIter = clPolyList.begin(); OutIter != clPolyList.end(); ++OutIter) {
if (OutIter->empty()) {
continue;
}
std::pair<Base::Vector3f, Base::Vector3f> currentSort;
float fDist = Base::Distance(OutIter->front(), OutIter->back());
currentSort.first = OutIter->front();
currentSort.second = OutIter->back();
for (auto InnerIter = clPolyList.begin(); InnerIter != clPolyList.end(); ++InnerIter) {
if (OutIter == InnerIter) {
continue;
}
if (Base::Distance(OutIter->front(), InnerIter->front()) < fDist) {
currentSort.second = InnerIter->front();
fDist = Base::Distance(OutIter->front(), InnerIter->front());
}
if (Base::Distance(OutIter->front(), InnerIter->back()) < fDist) {
currentSort.second = InnerIter->back();
fDist = Base::Distance(OutIter->front(), InnerIter->back());
}
}
rclLines.push_front(currentSort);
}
return true;
}
void MeshAlgorithm::GetFacetsFromPlane(
const MeshFacetGrid& rclGrid,
const Base::Vector3f& clNormal,
float d,
const Base::Vector3f& rclLeft,
const Base::Vector3f& rclRight,
std::vector<FacetIndex>& rclRes
) const
{
std::vector<FacetIndex> aulFacets;
Base::Vector3f clBase = d * clNormal;
Base::Vector3f clPtNormal(rclLeft - rclRight);
clPtNormal.Normalize();
// search grid
MeshGridIterator clGridIter(rclGrid);
for (clGridIter.Init(); clGridIter.More(); clGridIter.Next()) {
// add facets from grid if the plane if cut the grid-voxel
if (clGridIter.GetBoundBox().IsCutPlane(clBase, clNormal)) {
clGridIter.GetElements(aulFacets);
}
}
// testing facet against planes
for (FacetIndex facetIndex : aulFacets) {
MeshGeomFacet clSFacet = _rclMesh.GetFacet(facetIndex);
if (clSFacet.IntersectWithPlane(clBase, clNormal)) {
bool bInner = false;
for (int i = 0; (i < 3) && !bInner; i++) {
Base::Vector3f clPt = clSFacet._aclPoints[i];
if ((clPt.DistanceToPlane(rclLeft, clPtNormal) <= 0.0F)
&& (clPt.DistanceToPlane(rclRight, clPtNormal) >= 0.0F)) {
bInner = true;
}
}
if (bInner) {
rclRes.push_back(facetIndex);
}
}
}
}
void MeshAlgorithm::PointsFromFacetsIndices(
const std::vector<FacetIndex>& rvecIndices,
std::vector<Base::Vector3f>& rvecPoints
) const
{
const MeshFacetArray& rclFAry = _rclMesh._aclFacetArray;
const MeshPointArray& rclPAry = _rclMesh._aclPointArray;
std::set<PointIndex> setPoints;
for (FacetIndex facetIndex : rvecIndices) {
for (PointIndex pointIndex : rclFAry[facetIndex]._aulPoints) {
setPoints.insert(pointIndex);
}
}
rvecPoints.clear();
for (PointIndex pointIndex : setPoints) {
rvecPoints.push_back(rclPAry[pointIndex]);
}
}
bool MeshAlgorithm::Distance(
const Base::Vector3f& rclPt,
FacetIndex ulFacetIdx,
float fMaxDistance,
float& rfDistance
) const
{
const MeshFacetArray& rclFAry = _rclMesh._aclFacetArray;
const MeshPointArray& rclPAry = _rclMesh._aclPointArray;
const PointIndex* pulIdx = rclFAry[ulFacetIdx]._aulPoints;
BoundBox3f clBB;
clBB.Add(rclPAry[*(pulIdx++)]);
clBB.Add(rclPAry[*(pulIdx++)]);
clBB.Add(rclPAry[*pulIdx]);
clBB.Enlarge(fMaxDistance);
if (!clBB.IsInBox(rclPt)) {
return false;
}
rfDistance = _rclMesh.GetFacet(ulFacetIdx).DistanceToPoint(rclPt);
return rfDistance < fMaxDistance;
}
float MeshAlgorithm::CalculateMinimumGridLength(
float fLength,
const Base::BoundBox3f& rBBox,
unsigned long maxElements
) const
{
// Max. limit of grid elements
float fMaxGridElements = static_cast<float>(maxElements);
// estimate the minimum allowed grid length
float fMinGridLen = static_cast<float>(
std::pow((rBBox.LengthX() * rBBox.LengthY() * rBBox.LengthZ() / fMaxGridElements), 0.3333F)
);
return std::max<float>(fMinGridLen, fLength);
}
// ----------------------------------------------------
void MeshRefPointToFacets::Rebuild()
{
_map.clear();
const MeshPointArray& rPoints = _rclMesh.GetPoints();
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
_map.resize(rPoints.size());
auto pFBegin = rFacets.begin();
for (auto pFIter = rFacets.begin(); pFIter != rFacets.end(); ++pFIter) {
_map[pFIter->_aulPoints[0]].insert(pFIter - pFBegin);
_map[pFIter->_aulPoints[1]].insert(pFIter - pFBegin);
_map[pFIter->_aulPoints[2]].insert(pFIter - pFBegin);
}
}
Base::Vector3f MeshRefPointToFacets::GetNormal(PointIndex pos) const
{
const std::set<FacetIndex>& n = _map[pos];
Base::Vector3f normal;
MeshGeomFacet f;
for (FacetIndex it : n) {
f = _rclMesh.GetFacet(it);
normal += f.Area() * f.GetNormal();
}
normal.Normalize();
return normal;
}
std::set<PointIndex> MeshRefPointToFacets::NeighbourPoints(
const std::vector<PointIndex>& pt,
int level
) const
{
std::set<PointIndex> cp, nb, lp;
cp.insert(pt.begin(), pt.end());
lp.insert(pt.begin(), pt.end());
auto f_it = _rclMesh.GetFacets().begin();
for (int i = 0; i < level; i++) {
std::set<PointIndex> cur;
for (PointIndex it : lp) {
const std::set<FacetIndex>& ft = (*this)[it];
for (FacetIndex jt : ft) {
for (PointIndex index : f_it[jt]._aulPoints) {
if (cp.find(index) == cp.end() && nb.find(index) == nb.end()) {
nb.insert(index);
cur.insert(index);
}
}
}
}
lp = cur;
if (lp.empty()) {
break;
}
}
return nb;
}
std::set<PointIndex> MeshRefPointToFacets::NeighbourPoints(PointIndex pos) const
{
std::set<PointIndex> p;
const std::set<FacetIndex>& vf = _map[pos];
for (FacetIndex it : vf) {
PointIndex p1 {}, p2 {}, p3 {};
_rclMesh.GetFacetPoints(it, p1, p2, p3);
if (p1 != pos) {
p.insert(p1);
}
if (p2 != pos) {
p.insert(p2);
}
if (p3 != pos) {
p.insert(p3);
}
}
return p;
}
void MeshRefPointToFacets::Neighbours(FacetIndex ulFacetInd, float fMaxDist, MeshCollector& collect) const
{
std::set<FacetIndex> visited;
Base::Vector3f clCenter = _rclMesh.GetFacet(ulFacetInd).GetGravityPoint();
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
SearchNeighbours(rFacets, ulFacetInd, clCenter, fMaxDist * fMaxDist, visited, collect);
}
void MeshRefPointToFacets::SearchNeighbours(
const MeshFacetArray& rFacets,
FacetIndex index,
const Base::Vector3f& rclCenter,
float fMaxDist2,
std::set<FacetIndex>& visited,
MeshCollector& collect
) const
{
if (visited.find(index) != visited.end()) {
return;
}
const MeshFacet& face = rFacets[index];
if (Base::DistanceP2(rclCenter, _rclMesh.GetFacet(face).GetGravityPoint()) > fMaxDist2) {
return;
}
visited.insert(index);
collect.Append(_rclMesh, index);
for (PointIndex ptIndex : face._aulPoints) {
const std::set<FacetIndex>& f = (*this)[ptIndex];
for (FacetIndex j : f) {
SearchNeighbours(rFacets, j, rclCenter, fMaxDist2, visited, collect);
}
}
}
MeshFacetArray::_TConstIterator MeshRefPointToFacets::GetFacet(FacetIndex index) const
{
return _rclMesh.GetFacets().begin() + index;
}
const std::set<FacetIndex>& MeshRefPointToFacets::operator[](PointIndex pos) const
{
return _map[pos];
}
std::vector<FacetIndex> MeshRefPointToFacets::GetIndices(PointIndex pos1, PointIndex pos2) const
{
std::vector<FacetIndex> intersection;
std::back_insert_iterator<std::vector<FacetIndex>> result(intersection);
const std::set<FacetIndex>& set1 = _map[pos1];
const std::set<FacetIndex>& set2 = _map[pos2];
std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), result);
return intersection;
}
std::vector<FacetIndex> MeshRefPointToFacets::GetIndices(
PointIndex pos1,
PointIndex pos2,
PointIndex pos3
) const
{
std::vector<FacetIndex> intersection;
std::back_insert_iterator<std::vector<FacetIndex>> result(intersection);
std::vector<FacetIndex> set1 = GetIndices(pos1, pos2);
const std::set<FacetIndex>& set2 = _map[pos3];
std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), result);
return intersection;
}
void MeshRefPointToFacets::AddNeighbour(PointIndex pos, FacetIndex facet)
{
_map[pos].insert(facet);
}
void MeshRefPointToFacets::RemoveNeighbour(PointIndex pos, FacetIndex facet)
{
_map[pos].erase(facet);
}
void MeshRefPointToFacets::RemoveFacet(FacetIndex facetIndex)
{
PointIndex p0 {}, p1 {}, p2 {};
_rclMesh.GetFacetPoints(facetIndex, p0, p1, p2);
_map[p0].erase(facetIndex);
_map[p1].erase(facetIndex);
_map[p2].erase(facetIndex);
}
//----------------------------------------------------------------------------
void MeshRefFacetToFacets::Rebuild()
{
_map.clear();
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
_map.resize(rFacets.size());
MeshRefPointToFacets vertexFace(_rclMesh);
auto pFBegin = rFacets.begin();
for (auto pFIter = pFBegin; pFIter != rFacets.end(); ++pFIter) {
for (PointIndex ptIndex : pFIter->_aulPoints) {
const std::set<FacetIndex>& faces = vertexFace[ptIndex];
for (FacetIndex face : faces) {
_map[pFIter - pFBegin].insert(face);
}
}
}
}
const std::set<FacetIndex>& MeshRefFacetToFacets::operator[](FacetIndex pos) const
{
return _map[pos];
}
std::vector<FacetIndex> MeshRefFacetToFacets::GetIndices(FacetIndex pos1, FacetIndex pos2) const
{
std::vector<FacetIndex> intersection;
std::back_insert_iterator<std::vector<FacetIndex>> result(intersection);
const std::set<FacetIndex>& set1 = _map[pos1];
const std::set<FacetIndex>& set2 = _map[pos2];
std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), result);
return intersection;
}
//----------------------------------------------------------------------------
void MeshRefPointToPoints::Rebuild()
{
_map.clear();
const MeshPointArray& rPoints = _rclMesh.GetPoints();
_map.resize(rPoints.size());
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
for (const auto& rFacet : rFacets) {
PointIndex ulP0 = rFacet._aulPoints[0];
PointIndex ulP1 = rFacet._aulPoints[1];
PointIndex ulP2 = rFacet._aulPoints[2];
_map[ulP0].insert(ulP1);
_map[ulP0].insert(ulP2);
_map[ulP1].insert(ulP0);
_map[ulP1].insert(ulP2);
_map[ulP2].insert(ulP0);
_map[ulP2].insert(ulP1);
}
}
Base::Vector3f MeshRefPointToPoints::GetNormal(PointIndex pos) const
{
const MeshPointArray& rPoints = _rclMesh.GetPoints();
MeshCore::PlaneFit pf;
pf.AddPoint(rPoints[pos]);
MeshCore::MeshPoint center = rPoints[pos];
const std::set<PointIndex>& cv = _map[pos];
for (PointIndex cv_it : cv) {
pf.AddPoint(rPoints[cv_it]);
center += rPoints[cv_it];
}
pf.Fit();
Base::Vector3f normal = pf.GetNormal();
normal.Normalize();
return normal;
}
float MeshRefPointToPoints::GetAverageEdgeLength(PointIndex index) const
{
const MeshPointArray& rPoints = _rclMesh.GetPoints();
float len = 0.0F;
const std::set<PointIndex>& n = (*this)[index];
const Base::Vector3f& p = rPoints[index];
for (PointIndex it : n) {
len += Base::Distance(p, rPoints[it]);
}
return (len / n.size());
}
const std::set<PointIndex>& MeshRefPointToPoints::operator[](PointIndex pos) const
{
return _map[pos];
}
void MeshRefPointToPoints::AddNeighbour(PointIndex pos, PointIndex facet)
{
_map[pos].insert(facet);
}
void MeshRefPointToPoints::RemoveNeighbour(PointIndex pos, PointIndex facet)
{
_map[pos].erase(facet);
}
//----------------------------------------------------------------------------
void MeshRefEdgeToFacets::Rebuild()
{
_map.clear();
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
FacetIndex index = 0;
for (auto it = rFacets.begin(); it != rFacets.end(); ++it, ++index) {
for (int i = 0; i < 3; i++) {
MeshEdge e;
e.first = it->_aulPoints[i];
e.second = it->_aulPoints[(i + 1) % 3];
auto jt = _map.find(e);
if (jt == _map.end()) {
_map[e].first = index;
_map[e].second = FACET_INDEX_MAX;
}
else {
_map[e].second = index;
}
}
}
}
const std::pair<FacetIndex, FacetIndex>& MeshRefEdgeToFacets::operator[](const MeshEdge& edge) const
{
return _map.find(edge)->second;
}
//----------------------------------------------------------------------------
void MeshRefNormalToPoints::Rebuild()
{
_norm.clear();
const MeshPointArray& rPoints = _rclMesh.GetPoints();
_norm.resize(rPoints.size());
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
for (const auto& rFacet : rFacets) {
const MeshPoint& p0 = rPoints[rFacet._aulPoints[0]];
const MeshPoint& p1 = rPoints[rFacet._aulPoints[1]];
const MeshPoint& p2 = rPoints[rFacet._aulPoints[2]];
float l2p01 = Base::DistanceP2(p0, p1);
float l2p12 = Base::DistanceP2(p1, p2);
float l2p20 = Base::DistanceP2(p2, p0);
Base::Vector3f facenormal = _rclMesh.GetFacet(rFacet).GetNormal();
_norm[rFacet._aulPoints[0]] += facenormal * (1.0F / (l2p01 * l2p20));
_norm[rFacet._aulPoints[1]] += facenormal * (1.0F / (l2p12 * l2p01));
_norm[rFacet._aulPoints[2]] += facenormal * (1.0F / (l2p20 * l2p12));
}
for (auto& it : _norm) {
it.Normalize();
}
}
const Base::Vector3f& MeshRefNormalToPoints::operator[](PointIndex pos) const
{
return _norm[pos];
}