1
/***************************************************************************
2
* Copyright (c) 2005 Imetric 3D GmbH *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
27
#include <boost/core/ignore_unused.hpp>
32
#include <Base/Console.h>
33
#include <Mod/Mesh/App/WildMagic4/Wm4MeshCurvature.h>
35
#include "Evaluation.h"
37
#include "MeshKernel.h"
38
#include "TopoAlgorithm.h"
39
#include "Triangulation.h"
42
using namespace MeshCore;
44
MeshTopoAlgorithm::MeshTopoAlgorithm(MeshKernel& rclM)
48
MeshTopoAlgorithm::~MeshTopoAlgorithm()
56
bool MeshTopoAlgorithm::InsertVertex(FacetIndex ulFacetPos, const Base::Vector3f& rclPoint)
58
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
59
MeshFacet clNewFacet1, clNewFacet2;
62
PointIndex ulPtCnt = _rclMesh._aclPointArray.size();
63
PointIndex ulPtInd = this->GetOrAddIndex(rclPoint);
64
FacetIndex ulSize = _rclMesh._aclFacetArray.size();
66
if (ulPtInd < ulPtCnt) {
67
return false; // the given point is already part of the mesh => creating new facets would
68
// be an illegal operation
74
clNewFacet1._aulPoints[0] = rclF._aulPoints[1];
75
clNewFacet1._aulPoints[1] = rclF._aulPoints[2];
76
clNewFacet1._aulPoints[2] = ulPtInd;
77
clNewFacet1._aulNeighbours[0] = rclF._aulNeighbours[1];
78
clNewFacet1._aulNeighbours[1] = ulSize + 1;
79
clNewFacet1._aulNeighbours[2] = ulFacetPos;
81
clNewFacet2._aulPoints[0] = rclF._aulPoints[2];
82
clNewFacet2._aulPoints[1] = rclF._aulPoints[0];
83
clNewFacet2._aulPoints[2] = ulPtInd;
84
clNewFacet2._aulNeighbours[0] = rclF._aulNeighbours[2];
85
clNewFacet2._aulNeighbours[1] = ulFacetPos;
86
clNewFacet2._aulNeighbours[2] = ulSize;
87
// adjust the neighbour facet
88
if (rclF._aulNeighbours[1] != FACET_INDEX_MAX) {
89
_rclMesh._aclFacetArray[rclF._aulNeighbours[1]].ReplaceNeighbour(ulFacetPos, ulSize);
91
if (rclF._aulNeighbours[2] != FACET_INDEX_MAX) {
92
_rclMesh._aclFacetArray[rclF._aulNeighbours[2]].ReplaceNeighbour(ulFacetPos, ulSize + 1);
95
rclF._aulPoints[2] = ulPtInd;
96
rclF._aulNeighbours[1] = ulSize;
97
rclF._aulNeighbours[2] = ulSize + 1;
100
_rclMesh._aclFacetArray.push_back(clNewFacet1);
101
_rclMesh._aclFacetArray.push_back(clNewFacet2);
106
bool MeshTopoAlgorithm::SnapVertex(FacetIndex ulFacetPos, const Base::Vector3f& rP)
108
MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos];
109
if (!rFace.HasOpenEdge()) {
112
Base::Vector3f cNo1 = _rclMesh.GetNormal(rFace);
113
for (unsigned short i = 0; i < 3; i++) {
114
if (rFace._aulNeighbours[i] == FACET_INDEX_MAX) {
115
const Base::Vector3f& rPt1 = _rclMesh._aclPointArray[rFace._aulPoints[i]];
116
const Base::Vector3f& rPt2 = _rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]];
117
Base::Vector3f cNo2 = (rPt2 - rPt1) % cNo1;
118
Base::Vector3f cNo3 = (rP - rPt1) % (rPt2 - rPt1);
119
float fD2 = Base::DistanceP2(rPt1, rPt2);
120
float fTV = (rP - rPt1) * (rPt2 - rPt1);
122
// Point is on the edge
123
if (cNo3.Length() < FLOAT_EPS) {
124
return SplitOpenEdge(ulFacetPos, i, rP);
126
else if ((rP - rPt1) * cNo2 > 0.0f && fD2 >= fTV && fTV >= 0.0f) {
128
cTria._aulPoints[0] = this->GetOrAddIndex(rP);
129
cTria._aulPoints[1] = rFace._aulPoints[(i + 1) % 3];
130
cTria._aulPoints[2] = rFace._aulPoints[i];
131
cTria._aulNeighbours[1] = ulFacetPos;
132
rFace._aulNeighbours[i] = _rclMesh.CountFacets();
133
_rclMesh._aclFacetArray.push_back(cTria);
142
void MeshTopoAlgorithm::OptimizeTopology(float fMaxAngle)
144
// For each internal edge get the adjacent facets. When doing an edge swap we must update
146
std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>> aEdge2Face;
147
for (MeshFacetArray::_TIterator pI = _rclMesh._aclFacetArray.begin();
148
pI != _rclMesh._aclFacetArray.end();
150
for (int i = 0; i < 3; i++) {
152
if (pI->_aulNeighbours[i] != FACET_INDEX_MAX) {
154
std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]);
156
std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]);
157
aEdge2Face[std::pair<PointIndex, PointIndex>(ulPt0, ulPt1)].push_back(
158
pI - _rclMesh._aclFacetArray.begin());
163
// fill up this list with all internal edges and perform swap edges until this list is empty
164
std::list<std::pair<PointIndex, PointIndex>> aEdgeList;
165
std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>>::iterator pE;
166
for (pE = aEdge2Face.begin(); pE != aEdge2Face.end(); ++pE) {
167
if (pE->second.size() == 2) { // make sure that we really have an internal edge
168
aEdgeList.push_back(pE->first);
172
// to be sure to avoid an endless loop
173
unsigned long uMaxIter = 5 * aEdge2Face.size();
175
// Perform a swap edge where needed
176
while (!aEdgeList.empty() && uMaxIter > 0) {
177
// get the first edge and remove it from the list
178
std::pair<PointIndex, PointIndex> aEdge = aEdgeList.front();
179
aEdgeList.pop_front();
182
// get the adjacent facets to this edge
183
pE = aEdge2Face.find(aEdge);
185
// this edge has been removed some iterations before
186
if (pE == aEdge2Face.end()) {
190
// Is swap edge allowed and sensible?
191
if (!ShouldSwapEdge(pE->second[0], pE->second[1], fMaxAngle)) {
195
// ok, here we should perform a swap edge to minimize the maximum angle
196
if (/*fMax12 > fMax34*/ true) {
198
SwapEdge(pE->second[0], pE->second[1]);
200
MeshFacet& rF1 = _rclMesh._aclFacetArray[pE->second[0]];
201
MeshFacet& rF2 = _rclMesh._aclFacetArray[pE->second[1]];
202
unsigned short side1 = rF1.Side(aEdge.first, aEdge.second);
203
unsigned short side2 = rF2.Side(aEdge.first, aEdge.second);
205
// adjust the edge list
206
for (int i = 0; i < 3; i++) {
207
std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>>::iterator it;
210
std::min<PointIndex>(rF1._aulPoints[i], rF1._aulPoints[(i + 1) % 3]);
212
std::max<PointIndex>(rF1._aulPoints[i], rF1._aulPoints[(i + 1) % 3]);
213
it = aEdge2Face.find(std::make_pair(ulPt0, ulPt1));
214
if (it != aEdge2Face.end()) {
215
if (it->second[0] == pE->second[1]) {
216
it->second[0] = pE->second[0];
218
else if (it->second[1] == pE->second[1]) {
219
it->second[1] = pE->second[0];
221
aEdgeList.push_back(it->first);
225
ulPt0 = std::min<PointIndex>(rF2._aulPoints[i], rF2._aulPoints[(i + 1) % 3]);
226
ulPt1 = std::max<PointIndex>(rF2._aulPoints[i], rF2._aulPoints[(i + 1) % 3]);
227
it = aEdge2Face.find(std::make_pair(ulPt0, ulPt1));
228
if (it != aEdge2Face.end()) {
229
if (it->second[0] == pE->second[0]) {
230
it->second[0] = pE->second[1];
232
else if (it->second[1] == pE->second[0]) {
233
it->second[1] = pE->second[1];
235
aEdgeList.push_back(it->first);
239
// Now we must remove the edge and replace it through the new edge
240
PointIndex ulPt0 = std::min<PointIndex>(rF1._aulPoints[(side1 + 1) % 3],
241
rF2._aulPoints[(side2 + 1) % 3]);
242
PointIndex ulPt1 = std::max<PointIndex>(rF1._aulPoints[(side1 + 1) % 3],
243
rF2._aulPoints[(side2 + 1) % 3]);
244
std::pair<PointIndex, PointIndex> aNewEdge = std::make_pair(ulPt0, ulPt1);
245
aEdge2Face[aNewEdge] = pE->second;
246
aEdge2Face.erase(pE);
251
// Cosine of the maximum angle in triangle (v1,v2,v3)
253
cos_maxangle(const Base::Vector3f& v1, const Base::Vector3f& v2, const Base::Vector3f& v3)
255
float a = Base::Distance(v2, v3);
256
float b = Base::Distance(v3, v1);
257
float c = Base::Distance(v1, v2);
258
float A = a * (b * b + c * c - a * a);
259
float B = b * (c * c + a * a - b * b);
260
float C = c * (a * a + b * b - c * c);
261
return 0.5f * std::min<float>(std::min<float>(A, B), C)
262
/ (a * b * c); // min cosine == max angle
265
static float swap_benefit(const Base::Vector3f& v1,
266
const Base::Vector3f& v2,
267
const Base::Vector3f& v3,
268
const Base::Vector3f& v4)
270
Base::Vector3f n124 = (v4 - v2) % (v1 - v2);
271
Base::Vector3f n234 = (v3 - v2) % (v4 - v2);
272
if ((n124 * n234) <= 0.0f) {
273
return 0.0f; // avoid normal flip
276
return std::max<float>(-cos_maxangle(v1, v2, v3), -cos_maxangle(v1, v3, v4))
277
- std::max<float>(-cos_maxangle(v1, v2, v4), -cos_maxangle(v2, v3, v4));
280
float MeshTopoAlgorithm::SwapEdgeBenefit(FacetIndex f, int e) const
282
const MeshFacetArray& faces = _rclMesh.GetFacets();
283
const MeshPointArray& vertices = _rclMesh.GetPoints();
285
FacetIndex n = faces[f]._aulNeighbours[e];
286
if (n == FACET_INDEX_MAX) {
287
return 0.0f; // border edge
290
PointIndex v1 = faces[f]._aulPoints[e];
291
PointIndex v2 = faces[f]._aulPoints[(e + 1) % 3];
292
PointIndex v3 = faces[f]._aulPoints[(e + 2) % 3];
293
unsigned short s = faces[n].Side(faces[f]);
294
if (s == USHRT_MAX) {
295
std::cerr << "MeshTopoAlgorithm::SwapEdgeBenefit: error in neighbourhood "
296
<< "of faces " << f << " and " << n << std::endl;
297
return 0.0f; // topological error
299
PointIndex v4 = faces[n]._aulPoints[(s + 2) % 3];
301
std::cerr << "MeshTopoAlgorithm::SwapEdgeBenefit: duplicate faces " << f << " and " << n
303
return 0.0f; // duplicate faces
305
return swap_benefit(vertices[v2], vertices[v3], vertices[v1], vertices[v4]);
308
using FaceEdge = std::pair<FacetIndex, int>; // (face, edge) pair
309
using FaceEdgePriority = std::pair<float, FaceEdge>;
311
void MeshTopoAlgorithm::OptimizeTopology()
313
// Find all edges that can be swapped and insert them into a
315
const MeshFacetArray& faces = _rclMesh.GetFacets();
316
FacetIndex nf = _rclMesh.CountFacets();
317
std::priority_queue<FaceEdgePriority> todo;
318
for (FacetIndex i = 0; i < nf; i++) {
319
for (int j = 0; j < 3; j++) {
320
float b = SwapEdgeBenefit(i, j);
322
todo.push(std::make_pair(b, std::make_pair(i, j)));
327
// Edges are sorted in decreasing order with respect to their benefit
328
while (!todo.empty()) {
329
FacetIndex f = todo.top().second.first;
330
int e = todo.top().second.second;
332
// Check again if the swap should still be done
333
if (SwapEdgeBenefit(f, e) <= 0.0f) {
337
FacetIndex f2 = faces[f]._aulNeighbours[e];
339
// Insert new edges into queue, if necessary
340
for (int j = 0; j < 3; j++) {
341
float b = SwapEdgeBenefit(f, j);
343
todo.push(std::make_pair(b, std::make_pair(f, j)));
346
for (int j = 0; j < 3; j++) {
347
float b = SwapEdgeBenefit(f2, j);
349
todo.push(std::make_pair(b, std::make_pair(f2, j)));
355
void MeshTopoAlgorithm::DelaunayFlip(float fMaxAngle)
357
// For each internal edge get the adjacent facets.
358
std::set<std::pair<FacetIndex, FacetIndex>> aEdge2Face;
359
FacetIndex index = 0;
360
for (MeshFacetArray::_TIterator pI = _rclMesh._aclFacetArray.begin();
361
pI != _rclMesh._aclFacetArray.end();
363
for (FacetIndex nbIndex : pI->_aulNeighbours) {
365
if (nbIndex != FACET_INDEX_MAX) {
366
FacetIndex ulFt0 = std::min<FacetIndex>(index, nbIndex);
367
FacetIndex ulFt1 = std::max<FacetIndex>(index, nbIndex);
368
aEdge2Face.insert(std::pair<FacetIndex, FacetIndex>(ulFt0, ulFt1));
373
Base::Vector3f center;
374
while (!aEdge2Face.empty()) {
375
std::set<std::pair<FacetIndex, FacetIndex>>::iterator it = aEdge2Face.begin();
376
std::pair<FacetIndex, FacetIndex> edge = *it;
377
aEdge2Face.erase(it);
378
if (ShouldSwapEdge(edge.first, edge.second, fMaxAngle)) {
379
float radius = _rclMesh.GetFacet(edge.first).CenterOfCircumCircle(center);
381
const MeshFacet& face_1 = _rclMesh._aclFacetArray[edge.first];
382
const MeshFacet& face_2 = _rclMesh._aclFacetArray[edge.second];
383
unsigned short side = face_2.Side(edge.first);
384
MeshPoint vertex = _rclMesh.GetPoint(face_2._aulPoints[(side + 1) % 3]);
385
if (Base::DistanceP2(center, vertex) < radius) {
386
SwapEdge(edge.first, edge.second);
387
for (int i = 0; i < 3; i++) {
388
if (face_1._aulNeighbours[i] != FACET_INDEX_MAX
389
&& face_1._aulNeighbours[i] != edge.second) {
391
std::min<FacetIndex>(edge.first, face_1._aulNeighbours[i]);
393
std::max<FacetIndex>(edge.first, face_1._aulNeighbours[i]);
394
aEdge2Face.insert(std::pair<FacetIndex, FacetIndex>(ulFt0, ulFt1));
396
if (face_2._aulNeighbours[i] != FACET_INDEX_MAX
397
&& face_2._aulNeighbours[i] != edge.first) {
399
std::min<FacetIndex>(edge.second, face_2._aulNeighbours[i]);
401
std::max<FacetIndex>(edge.second, face_2._aulNeighbours[i]);
402
aEdge2Face.insert(std::pair<FacetIndex, FacetIndex>(ulFt0, ulFt1));
410
int MeshTopoAlgorithm::DelaunayFlip()
413
_rclMesh._aclFacetArray.ResetFlag(MeshFacet::TMP0);
414
size_t cnt_facets = _rclMesh._aclFacetArray.size();
415
for (size_t i = 0; i < cnt_facets; i++) {
416
const MeshFacet& f_face = _rclMesh._aclFacetArray[i];
417
if (f_face.IsFlag(MeshFacet::TMP0)) {
420
for (int j = 0; j < 3; j++) {
421
FacetIndex n = f_face._aulNeighbours[j];
422
if (n != FACET_INDEX_MAX) {
423
const MeshFacet& n_face = _rclMesh._aclFacetArray[n];
424
if (n_face.IsFlag(MeshFacet::TMP0)) {
427
unsigned short k = n_face.Side(f_face);
428
MeshGeomFacet f1 = _rclMesh.GetFacet(f_face);
429
MeshGeomFacet f2 = _rclMesh.GetFacet(n_face);
430
Base::Vector3f c1, c2, p1, p2;
431
p1 = f1._aclPoints[(j + 2) % 3];
432
p2 = f2._aclPoints[(k + 2) % 3];
433
float r1 = f1.CenterOfCircumCircle(c1);
435
float r2 = f2.CenterOfCircumCircle(c2);
437
float d1 = Base::DistanceP2(c1, p2);
438
float d2 = Base::DistanceP2(c2, p1);
439
if (d1 < r1 || d2 < r2) {
442
f_face.SetFlag(MeshFacet::TMP0);
443
n_face.SetFlag(MeshFacet::TMP0);
452
void MeshTopoAlgorithm::AdjustEdgesToCurvatureDirection()
454
std::vector<Wm4::Vector3<float>> aPnts;
455
MeshPointIterator cPIt(_rclMesh);
456
aPnts.reserve(_rclMesh.CountPoints());
457
for (cPIt.Init(); cPIt.More(); cPIt.Next()) {
458
aPnts.emplace_back(cPIt->x, cPIt->y, cPIt->z);
461
// get all point connections
462
std::vector<int> aIdx;
463
const MeshFacetArray& raFts = _rclMesh.GetFacets();
464
aIdx.reserve(3 * raFts.size());
466
// Build map of edges to the referencing facets
468
std::map<std::pair<PointIndex, PointIndex>, std::list<FacetIndex>> aclEdgeMap;
469
for (std::vector<MeshFacet>::const_iterator jt = raFts.begin(); jt != raFts.end(); ++jt, k++) {
470
for (int i = 0; i < 3; i++) {
471
PointIndex ulT0 = jt->_aulPoints[i];
472
PointIndex ulT1 = jt->_aulPoints[(i + 1) % 3];
473
PointIndex ulP0 = std::min<PointIndex>(ulT0, ulT1);
474
PointIndex ulP1 = std::max<PointIndex>(ulT0, ulT1);
475
aclEdgeMap[std::make_pair(ulP0, ulP1)].push_front(k);
476
aIdx.push_back(static_cast<int>(jt->_aulPoints[i]));
480
// compute vertex based curvatures
481
Wm4::MeshCurvature<float> meshCurv(static_cast<int>(_rclMesh.CountPoints()),
483
static_cast<int>(_rclMesh.CountFacets()),
486
// get curvature information now
487
const Wm4::Vector3<float>* aMaxCurvDir = meshCurv.GetMaxDirections();
488
const Wm4::Vector3<float>* aMinCurvDir = meshCurv.GetMinDirections();
489
const float* aMaxCurv = meshCurv.GetMaxCurvatures();
490
const float* aMinCurv = meshCurv.GetMinCurvatures();
492
raFts.ResetFlag(MeshFacet::VISIT);
493
const MeshPointArray& raPts = _rclMesh.GetPoints();
494
for (auto& kt : aclEdgeMap) {
495
if (kt.second.size() == 2) {
496
PointIndex uPt1 = kt.first.first;
497
PointIndex uPt2 = kt.first.second;
498
FacetIndex uFt1 = kt.second.front();
499
FacetIndex uFt2 = kt.second.back();
501
const MeshFacet& rFace1 = raFts[uFt1];
502
const MeshFacet& rFace2 = raFts[uFt2];
503
if (rFace1.IsFlag(MeshFacet::VISIT) || rFace2.IsFlag(MeshFacet::VISIT)) {
507
PointIndex uPt3 {}, uPt4 {};
508
unsigned short side = rFace1.Side(uPt1, uPt2);
509
uPt3 = rFace1._aulPoints[(side + 2) % 3];
510
side = rFace2.Side(uPt1, uPt2);
511
uPt4 = rFace2._aulPoints[(side + 2) % 3];
513
Wm4::Vector3<float> dir;
514
float fActCurvature {};
515
if (fabs(aMinCurv[uPt1]) > fabs(aMaxCurv[uPt1])) {
516
fActCurvature = aMinCurv[uPt1];
517
dir = aMaxCurvDir[uPt1];
520
fActCurvature = aMaxCurv[uPt1];
521
dir = aMinCurvDir[uPt1];
524
Base::Vector3f cMinDir(dir.X(), dir.Y(), dir.Z());
525
Base::Vector3f cEdgeDir1 = raPts[uPt1] - raPts[uPt2];
526
Base::Vector3f cEdgeDir2 = raPts[uPt3] - raPts[uPt4];
528
cEdgeDir1.Normalize();
529
cEdgeDir2.Normalize();
531
// get the plane and calculate the distance to the fourth point
532
MeshGeomFacet cPlane(raPts[uPt1], raPts[uPt2], raPts[uPt3]);
533
// positive or negative distance
534
float fDist = raPts[uPt4].DistanceToPlane(cPlane._aclPoints[0], cPlane.GetNormal());
536
float fLength12 = Base::Distance(raPts[uPt1], raPts[uPt2]);
537
float fLength34 = Base::Distance(raPts[uPt3], raPts[uPt4]);
538
if (fabs(cEdgeDir1 * cMinDir) < fabs(cEdgeDir2 * cMinDir)) {
539
if (IsSwapEdgeLegal(uFt1, uFt2) && fLength34 < 1.05f * fLength12
540
&& fActCurvature * fDist > 0.0f) {
541
SwapEdge(uFt1, uFt2);
542
rFace1.SetFlag(MeshFacet::VISIT);
543
rFace2.SetFlag(MeshFacet::VISIT);
550
bool MeshTopoAlgorithm::InsertVertexAndSwapEdge(FacetIndex ulFacetPos,
551
const Base::Vector3f& rclPoint,
554
if (!InsertVertex(ulFacetPos, rclPoint)) {
558
// get the created elements
559
FacetIndex ulF1Ind = _rclMesh._aclFacetArray.size() - 2;
560
FacetIndex ulF2Ind = _rclMesh._aclFacetArray.size() - 1;
561
MeshFacet& rclF1 = _rclMesh._aclFacetArray[ulFacetPos];
562
MeshFacet& rclF2 = _rclMesh._aclFacetArray[ulF1Ind];
563
MeshFacet& rclF3 = _rclMesh._aclFacetArray[ulF2Ind];
566
for (FacetIndex uNeighbour : rclF1._aulNeighbours) {
567
if (uNeighbour != FACET_INDEX_MAX && uNeighbour != ulF1Ind && uNeighbour != ulF2Ind) {
568
if (ShouldSwapEdge(ulFacetPos, uNeighbour, fMaxAngle)) {
569
SwapEdge(ulFacetPos, uNeighbour);
574
for (FacetIndex uNeighbour : rclF2._aulNeighbours) {
576
if (uNeighbour != FACET_INDEX_MAX && uNeighbour != ulFacetPos && uNeighbour != ulF2Ind) {
577
if (ShouldSwapEdge(ulF1Ind, uNeighbour, fMaxAngle)) {
578
SwapEdge(ulF1Ind, uNeighbour);
585
for (FacetIndex uNeighbour : rclF3._aulNeighbours) {
586
if (uNeighbour != FACET_INDEX_MAX && uNeighbour != ulFacetPos && uNeighbour != ulF1Ind) {
587
if (ShouldSwapEdge(ulF2Ind, uNeighbour, fMaxAngle)) {
588
SwapEdge(ulF2Ind, uNeighbour);
597
bool MeshTopoAlgorithm::IsSwapEdgeLegal(FacetIndex ulFacetPos, FacetIndex ulNeighbour) const
599
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
600
MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour];
602
unsigned short uFSide = rclF.Side(rclN);
603
unsigned short uNSide = rclN.Side(rclF);
605
if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) {
606
return false; // not neighbours
609
Base::Vector3f cP1 = _rclMesh._aclPointArray[rclF._aulPoints[uFSide]];
610
Base::Vector3f cP2 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 1) % 3]];
611
Base::Vector3f cP3 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 2) % 3]];
612
Base::Vector3f cP4 = _rclMesh._aclPointArray[rclN._aulPoints[(uNSide + 2) % 3]];
614
// do not allow to create degenerated triangles
615
MeshGeomFacet cT3(cP4, cP3, cP1);
616
if (cT3.IsDegenerated(MeshDefinitions::_fMinPointDistanceP2)) {
619
MeshGeomFacet cT4(cP3, cP4, cP2);
620
if (cT4.IsDegenerated(MeshDefinitions::_fMinPointDistanceP2)) {
624
// We must make sure that the two adjacent triangles builds a convex polygon, otherwise
625
// the swap edge operation is illegal
626
Base::Vector3f cU = cP2 - cP1;
627
Base::Vector3f cV = cP4 - cP3;
628
// build a helper plane through cP1 that must separate cP3 and cP4
629
Base::Vector3f cN1 = (cU % cV) % cU;
630
if (((cP3 - cP1) * cN1) * ((cP4 - cP1) * cN1) >= 0.0f) {
631
return false; // not convex
633
// build a helper plane through cP3 that must separate cP1 and cP2
634
Base::Vector3f cN2 = (cU % cV) % cV;
635
if (((cP1 - cP3) * cN2) * ((cP2 - cP3) * cN2) >= 0.0f) {
636
return false; // not convex
642
bool MeshTopoAlgorithm::ShouldSwapEdge(FacetIndex ulFacetPos,
643
FacetIndex ulNeighbour,
644
float fMaxAngle) const
646
if (!IsSwapEdgeLegal(ulFacetPos, ulNeighbour)) {
650
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
651
MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour];
653
unsigned short uFSide = rclF.Side(rclN);
654
unsigned short uNSide = rclN.Side(rclF);
656
Base::Vector3f cP1 = _rclMesh._aclPointArray[rclF._aulPoints[uFSide]];
657
Base::Vector3f cP2 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 1) % 3]];
658
Base::Vector3f cP3 = _rclMesh._aclPointArray[rclF._aulPoints[(uFSide + 2) % 3]];
659
Base::Vector3f cP4 = _rclMesh._aclPointArray[rclN._aulPoints[(uNSide + 2) % 3]];
661
MeshGeomFacet cT1(cP1, cP2, cP3);
662
float fMax1 = cT1.MaximumAngle();
663
MeshGeomFacet cT2(cP2, cP1, cP4);
664
float fMax2 = cT2.MaximumAngle();
665
MeshGeomFacet cT3(cP4, cP3, cP1);
666
float fMax3 = cT3.MaximumAngle();
667
MeshGeomFacet cT4(cP3, cP4, cP2);
668
float fMax4 = cT4.MaximumAngle();
670
// get the angle between the triangles
671
Base::Vector3f cN1 = cT1.GetNormal();
672
Base::Vector3f cN2 = cT2.GetNormal();
673
if (cN1.GetAngle(cN2) > fMaxAngle) {
677
float fMax12 = std::max<float>(fMax1, fMax2);
678
float fMax34 = std::max<float>(fMax3, fMax4);
680
return fMax12 > fMax34;
683
void MeshTopoAlgorithm::SwapEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour)
685
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
686
MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour];
688
unsigned short uFSide = rclF.Side(rclN);
689
unsigned short uNSide = rclN.Side(rclF);
691
if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) {
692
return; // not neighbours
695
// adjust the neighbourhood
696
if (rclF._aulNeighbours[(uFSide + 1) % 3] != FACET_INDEX_MAX) {
697
_rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 1) % 3]].ReplaceNeighbour(
701
if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) {
702
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour(ulNeighbour,
706
// swap the point and neighbour indices
707
rclF._aulPoints[(uFSide + 1) % 3] = rclN._aulPoints[(uNSide + 2) % 3];
708
rclN._aulPoints[(uNSide + 1) % 3] = rclF._aulPoints[(uFSide + 2) % 3];
709
rclF._aulNeighbours[uFSide] = rclN._aulNeighbours[(uNSide + 1) % 3];
710
rclN._aulNeighbours[uNSide] = rclF._aulNeighbours[(uFSide + 1) % 3];
711
rclF._aulNeighbours[(uFSide + 1) % 3] = ulNeighbour;
712
rclN._aulNeighbours[(uNSide + 1) % 3] = ulFacetPos;
715
bool MeshTopoAlgorithm::SplitEdge(FacetIndex ulFacetPos,
716
FacetIndex ulNeighbour,
717
const Base::Vector3f& rP)
719
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
720
MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour];
722
unsigned short uFSide = rclF.Side(rclN);
723
unsigned short uNSide = rclN.Side(rclF);
725
if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) {
726
return false; // not neighbours
729
PointIndex uPtCnt = _rclMesh._aclPointArray.size();
730
PointIndex uPtInd = this->GetOrAddIndex(rP);
731
FacetIndex ulSize = _rclMesh._aclFacetArray.size();
733
// the given point is already part of the mesh => creating new facets would
734
// be an illegal operation
735
if (uPtInd < uPtCnt) {
739
// adjust the neighbourhood
740
if (rclF._aulNeighbours[(uFSide + 1) % 3] != FACET_INDEX_MAX) {
741
_rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 1) % 3]].ReplaceNeighbour(ulFacetPos,
744
if (rclN._aulNeighbours[(uNSide + 2) % 3] != FACET_INDEX_MAX) {
745
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 2) % 3]].ReplaceNeighbour(ulNeighbour,
749
MeshFacet cNew1, cNew2;
750
cNew1._aulPoints[0] = uPtInd;
751
cNew1._aulPoints[1] = rclF._aulPoints[(uFSide + 1) % 3];
752
cNew1._aulPoints[2] = rclF._aulPoints[(uFSide + 2) % 3];
753
cNew1._aulNeighbours[0] = ulSize + 1;
754
cNew1._aulNeighbours[1] = rclF._aulNeighbours[(uFSide + 1) % 3];
755
cNew1._aulNeighbours[2] = ulFacetPos;
757
cNew2._aulPoints[0] = rclN._aulPoints[uNSide];
758
cNew2._aulPoints[1] = uPtInd;
759
cNew2._aulPoints[2] = rclN._aulPoints[(uNSide + 2) % 3];
760
cNew2._aulNeighbours[0] = ulSize;
761
cNew2._aulNeighbours[1] = ulNeighbour;
762
cNew2._aulNeighbours[2] = rclN._aulNeighbours[(uNSide + 2) % 3];
765
rclF._aulPoints[(uFSide + 1) % 3] = uPtInd;
766
rclF._aulNeighbours[(uFSide + 1) % 3] = ulSize;
767
rclN._aulPoints[uNSide] = uPtInd;
768
rclN._aulNeighbours[(uNSide + 2) % 3] = ulSize + 1;
771
_rclMesh._aclFacetArray.push_back(cNew1);
772
_rclMesh._aclFacetArray.push_back(cNew2);
777
bool MeshTopoAlgorithm::SplitOpenEdge(FacetIndex ulFacetPos,
778
unsigned short uSide,
779
const Base::Vector3f& rP)
781
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
782
if (rclF._aulNeighbours[uSide] != FACET_INDEX_MAX) {
783
return false; // not open
786
PointIndex uPtCnt = _rclMesh._aclPointArray.size();
787
PointIndex uPtInd = this->GetOrAddIndex(rP);
788
FacetIndex ulSize = _rclMesh._aclFacetArray.size();
790
if (uPtInd < uPtCnt) {
791
return false; // the given point is already part of the mesh => creating new facets would
792
// be an illegal operation
795
// adjust the neighbourhood
796
if (rclF._aulNeighbours[(uSide + 1) % 3] != FACET_INDEX_MAX) {
797
_rclMesh._aclFacetArray[rclF._aulNeighbours[(uSide + 1) % 3]].ReplaceNeighbour(ulFacetPos,
802
cNew._aulPoints[0] = uPtInd;
803
cNew._aulPoints[1] = rclF._aulPoints[(uSide + 1) % 3];
804
cNew._aulPoints[2] = rclF._aulPoints[(uSide + 2) % 3];
805
cNew._aulNeighbours[0] = FACET_INDEX_MAX;
806
cNew._aulNeighbours[1] = rclF._aulNeighbours[(uSide + 1) % 3];
807
cNew._aulNeighbours[2] = ulFacetPos;
810
rclF._aulPoints[(uSide + 1) % 3] = uPtInd;
811
rclF._aulNeighbours[(uSide + 1) % 3] = ulSize;
814
_rclMesh._aclFacetArray.push_back(cNew);
818
bool MeshTopoAlgorithm::Vertex_Less::operator()(const Base::Vector3f& u,
819
const Base::Vector3f& v) const
821
if (fabs(u.x - v.x) > FLOAT_EPS) {
824
if (fabs(u.y - v.y) > FLOAT_EPS) {
827
if (fabs(u.z - v.z) > FLOAT_EPS) {
833
void MeshTopoAlgorithm::BeginCache()
838
_cache = new tCache();
839
PointIndex nbPoints = _rclMesh._aclPointArray.size();
840
for (unsigned int pntCpt = 0; pntCpt < nbPoints; ++pntCpt) {
841
_cache->insert(std::make_pair(_rclMesh._aclPointArray[pntCpt], pntCpt));
845
void MeshTopoAlgorithm::EndCache()
854
PointIndex MeshTopoAlgorithm::GetOrAddIndex(const MeshPoint& rclPoint)
857
return _rclMesh._aclPointArray.GetOrAddIndex(rclPoint);
860
unsigned long sz = _rclMesh._aclPointArray.size();
861
std::pair<tCache::iterator, bool> retval = _cache->insert(std::make_pair(rclPoint, sz));
863
_rclMesh._aclPointArray.push_back(rclPoint);
865
return retval.first->second;
868
std::vector<FacetIndex> MeshTopoAlgorithm::GetFacetsToPoint(FacetIndex uFacetPos,
869
PointIndex uPointPos) const
871
// get all facets this point is referenced by
872
std::list<FacetIndex> aReference;
873
aReference.push_back(uFacetPos);
874
std::set<FacetIndex> aRefFacet;
875
while (!aReference.empty()) {
876
FacetIndex uIndex = aReference.front();
877
aReference.pop_front();
878
aRefFacet.insert(uIndex);
879
MeshFacet& rFace = _rclMesh._aclFacetArray[uIndex];
880
for (int i = 0; i < 3; i++) {
881
if (rFace._aulPoints[i] == uPointPos) {
882
if (rFace._aulNeighbours[i] != FACET_INDEX_MAX) {
883
if (aRefFacet.find(rFace._aulNeighbours[i]) == aRefFacet.end()) {
884
aReference.push_back(rFace._aulNeighbours[i]);
887
if (rFace._aulNeighbours[(i + 2) % 3] != FACET_INDEX_MAX) {
888
if (aRefFacet.find(rFace._aulNeighbours[(i + 2) % 3]) == aRefFacet.end()) {
889
aReference.push_back(rFace._aulNeighbours[(i + 2) % 3]);
898
std::vector<FacetIndex> aRefs;
899
aRefs.insert(aRefs.end(), aRefFacet.begin(), aRefFacet.end());
903
void MeshTopoAlgorithm::Cleanup()
905
_rclMesh.RemoveInvalids();
906
_needsCleanup = false;
909
bool MeshTopoAlgorithm::CollapseVertex(const VertexCollapse& vc)
911
if (vc._circumFacets.size() != vc._circumPoints.size()) {
915
if (vc._circumFacets.size() != 3) {
919
if (!_rclMesh._aclPointArray[vc._point].IsValid()) {
920
return false; // the point is marked invalid from a previous run
923
MeshFacet& rFace1 = _rclMesh._aclFacetArray[vc._circumFacets[0]];
924
MeshFacet& rFace2 = _rclMesh._aclFacetArray[vc._circumFacets[1]];
925
MeshFacet& rFace3 = _rclMesh._aclFacetArray[vc._circumFacets[2]];
927
// get the point that is not shared by rFace1
928
PointIndex ptIndex = POINT_INDEX_MAX;
929
std::vector<PointIndex>::const_iterator it;
930
for (it = vc._circumPoints.begin(); it != vc._circumPoints.end(); ++it) {
931
if (!rFace1.HasPoint(*it)) {
937
if (ptIndex == POINT_INDEX_MAX) {
941
FacetIndex neighbour1 = FACET_INDEX_MAX;
942
FacetIndex neighbour2 = FACET_INDEX_MAX;
944
const std::vector<FacetIndex>& faces = vc._circumFacets;
945
// get neighbours that are not part of the faces to be removed
946
for (int i = 0; i < 3; i++) {
947
if (std::find(faces.begin(), faces.end(), rFace2._aulNeighbours[i]) == faces.end()) {
948
neighbour1 = rFace2._aulNeighbours[i];
950
if (std::find(faces.begin(), faces.end(), rFace3._aulNeighbours[i]) == faces.end()) {
951
neighbour2 = rFace3._aulNeighbours[i];
955
// adjust point and neighbour indices
956
rFace1.Transpose(vc._point, ptIndex);
957
rFace1.ReplaceNeighbour(vc._circumFacets[1], neighbour1);
958
rFace1.ReplaceNeighbour(vc._circumFacets[2], neighbour2);
960
if (neighbour1 != FACET_INDEX_MAX) {
961
MeshFacet& rFace4 = _rclMesh._aclFacetArray[neighbour1];
962
rFace4.ReplaceNeighbour(vc._circumFacets[1], vc._circumFacets[0]);
964
if (neighbour2 != FACET_INDEX_MAX) {
965
MeshFacet& rFace5 = _rclMesh._aclFacetArray[neighbour2];
966
rFace5.ReplaceNeighbour(vc._circumFacets[2], vc._circumFacets[0]);
969
// the two facets and the point can be marked for removal
972
_rclMesh._aclPointArray[vc._point].SetInvalid();
974
_needsCleanup = true;
979
bool MeshTopoAlgorithm::CollapseEdge(FacetIndex ulFacetPos, FacetIndex ulNeighbour)
981
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
982
MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour];
984
unsigned short uFSide = rclF.Side(rclN);
985
unsigned short uNSide = rclN.Side(rclF);
987
if (uFSide == USHRT_MAX || uNSide == USHRT_MAX) {
988
return false; // not neighbours
991
if (!rclF.IsValid() || !rclN.IsValid()) {
992
return false; // the facets are marked invalid from a previous run
995
// get the point index we want to remove
996
PointIndex ulPointPos = rclF._aulPoints[uFSide];
997
PointIndex ulPointNew = rclN._aulPoints[uNSide];
999
// get all facets this point is referenced by
1000
std::vector<FacetIndex> aRefs = GetFacetsToPoint(ulFacetPos, ulPointPos);
1001
for (FacetIndex it : aRefs) {
1002
MeshFacet& rFace = _rclMesh._aclFacetArray[it];
1003
rFace.Transpose(ulPointPos, ulPointNew);
1006
// set the new neighbourhood
1007
if (rclF._aulNeighbours[(uFSide + 1) % 3] != FACET_INDEX_MAX) {
1008
_rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 1) % 3]].ReplaceNeighbour(
1010
rclF._aulNeighbours[(uFSide + 2) % 3]);
1012
if (rclF._aulNeighbours[(uFSide + 2) % 3] != FACET_INDEX_MAX) {
1013
_rclMesh._aclFacetArray[rclF._aulNeighbours[(uFSide + 2) % 3]].ReplaceNeighbour(
1015
rclF._aulNeighbours[(uFSide + 1) % 3]);
1017
if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) {
1018
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour(
1020
rclN._aulNeighbours[(uNSide + 2) % 3]);
1022
if (rclN._aulNeighbours[(uNSide + 2) % 3] != FACET_INDEX_MAX) {
1023
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 2) % 3]].ReplaceNeighbour(
1025
rclN._aulNeighbours[(uNSide + 1) % 3]);
1028
// isolate the both facets and the point
1029
rclF._aulNeighbours[0] = FACET_INDEX_MAX;
1030
rclF._aulNeighbours[1] = FACET_INDEX_MAX;
1031
rclF._aulNeighbours[2] = FACET_INDEX_MAX;
1033
rclN._aulNeighbours[0] = FACET_INDEX_MAX;
1034
rclN._aulNeighbours[1] = FACET_INDEX_MAX;
1035
rclN._aulNeighbours[2] = FACET_INDEX_MAX;
1037
_rclMesh._aclPointArray[ulPointPos].SetInvalid();
1039
_needsCleanup = true;
1044
bool MeshTopoAlgorithm::IsCollapseEdgeLegal(const EdgeCollapse& ec) const
1046
// http://stackoverflow.com/a/27049418/148668
1047
// Check connectivity
1049
std::vector<PointIndex> commonPoints;
1050
std::set_intersection(ec._adjacentFrom.begin(),
1051
ec._adjacentFrom.end(),
1052
ec._adjacentTo.begin(),
1053
ec._adjacentTo.end(),
1054
std::back_insert_iterator<std::vector<PointIndex>>(commonPoints));
1055
if (commonPoints.size() > 2) {
1060
std::vector<FacetIndex>::const_iterator it;
1061
for (it = ec._changeFacets.begin(); it != ec._changeFacets.end(); ++it) {
1062
MeshFacet f = _rclMesh._aclFacetArray[*it];
1067
// ignore the facet(s) at this edge
1068
if (f.HasPoint(ec._fromPoint) && f.HasPoint(ec._toPoint)) {
1072
MeshGeomFacet tria1 = _rclMesh.GetFacet(f);
1073
f.Transpose(ec._fromPoint, ec._toPoint);
1074
MeshGeomFacet tria2 = _rclMesh.GetFacet(f);
1076
if (tria1.GetNormal() * tria2.GetNormal() < 0.0f) {
1081
// If the data structure is valid and the algorithm works as expected
1082
// it should never happen to reject the edge-collapse here!
1083
for (it = ec._removeFacets.begin(); it != ec._removeFacets.end(); ++it) {
1084
MeshFacet f = _rclMesh._aclFacetArray[*it];
1090
if (!_rclMesh._aclPointArray[ec._fromPoint].IsValid()) {
1094
if (!_rclMesh._aclPointArray[ec._toPoint].IsValid()) {
1101
bool MeshTopoAlgorithm::CollapseEdge(const EdgeCollapse& ec)
1103
std::vector<FacetIndex>::const_iterator it;
1104
for (it = ec._removeFacets.begin(); it != ec._removeFacets.end(); ++it) {
1105
MeshFacet& f = _rclMesh._aclFacetArray[*it];
1108
// adjust the neighbourhood
1109
std::vector<FacetIndex> neighbours;
1110
for (FacetIndex nbIndex : f._aulNeighbours) {
1111
// get the neighbours of the facet that won't be invalidated
1112
if (nbIndex != FACET_INDEX_MAX) {
1113
if (std::find(ec._removeFacets.begin(), ec._removeFacets.end(), nbIndex)
1114
== ec._removeFacets.end()) {
1115
neighbours.push_back(nbIndex);
1120
if (neighbours.size() == 2) {
1121
MeshFacet& n1 = _rclMesh._aclFacetArray[neighbours[0]];
1122
n1.ReplaceNeighbour(*it, neighbours[1]);
1123
MeshFacet& n2 = _rclMesh._aclFacetArray[neighbours[1]];
1124
n2.ReplaceNeighbour(*it, neighbours[0]);
1126
else if (neighbours.size() == 1) {
1127
MeshFacet& n1 = _rclMesh._aclFacetArray[neighbours[0]];
1128
n1.ReplaceNeighbour(*it, FACET_INDEX_MAX);
1132
for (it = ec._changeFacets.begin(); it != ec._changeFacets.end(); ++it) {
1133
MeshFacet& f = _rclMesh._aclFacetArray[*it];
1134
f.Transpose(ec._fromPoint, ec._toPoint);
1137
_rclMesh._aclPointArray[ec._fromPoint].SetInvalid();
1139
_needsCleanup = true;
1143
bool MeshTopoAlgorithm::CollapseFacet(FacetIndex ulFacetPos)
1145
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
1146
if (!rclF.IsValid()) {
1147
return false; // the facet is marked invalid from a previous run
1150
// get the point index we want to remove
1151
PointIndex ulPointInd0 = rclF._aulPoints[0];
1152
PointIndex ulPointInd1 = rclF._aulPoints[1];
1153
PointIndex ulPointInd2 = rclF._aulPoints[2];
1155
// move the vertex to the gravity center
1156
Base::Vector3f cCenter = _rclMesh.GetGravityPoint(rclF);
1157
_rclMesh._aclPointArray[ulPointInd0] = cCenter;
1159
// set the new point indices for all facets that share one of the points to be deleted
1160
std::vector<FacetIndex> aRefs = GetFacetsToPoint(ulFacetPos, ulPointInd1);
1161
for (FacetIndex it : aRefs) {
1162
MeshFacet& rFace = _rclMesh._aclFacetArray[it];
1163
rFace.Transpose(ulPointInd1, ulPointInd0);
1166
aRefs = GetFacetsToPoint(ulFacetPos, ulPointInd2);
1167
for (FacetIndex it : aRefs) {
1168
MeshFacet& rFace = _rclMesh._aclFacetArray[it];
1169
rFace.Transpose(ulPointInd2, ulPointInd0);
1172
// set the neighbourhood of the circumjacent facets
1173
for (FacetIndex nbIndex : rclF._aulNeighbours) {
1174
if (nbIndex == FACET_INDEX_MAX) {
1177
MeshFacet& rclN = _rclMesh._aclFacetArray[nbIndex];
1178
unsigned short uNSide = rclN.Side(rclF);
1180
if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) {
1181
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour(
1183
rclN._aulNeighbours[(uNSide + 2) % 3]);
1185
if (rclN._aulNeighbours[(uNSide + 2) % 3] != FACET_INDEX_MAX) {
1186
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 2) % 3]].ReplaceNeighbour(
1188
rclN._aulNeighbours[(uNSide + 1) % 3]);
1191
// Isolate the neighbours from the topology
1192
rclN._aulNeighbours[0] = FACET_INDEX_MAX;
1193
rclN._aulNeighbours[1] = FACET_INDEX_MAX;
1194
rclN._aulNeighbours[2] = FACET_INDEX_MAX;
1198
// Isolate this facet and make two of its points invalid
1199
rclF._aulNeighbours[0] = FACET_INDEX_MAX;
1200
rclF._aulNeighbours[1] = FACET_INDEX_MAX;
1201
rclF._aulNeighbours[2] = FACET_INDEX_MAX;
1203
_rclMesh._aclPointArray[ulPointInd1].SetInvalid();
1204
_rclMesh._aclPointArray[ulPointInd2].SetInvalid();
1206
_needsCleanup = true;
1211
void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos,
1212
const Base::Vector3f& rP1,
1213
const Base::Vector3f& rP2)
1215
float fEps = MESH_MIN_EDGE_LEN;
1216
MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos];
1217
MeshPoint& rVertex0 = _rclMesh._aclPointArray[rFace._aulPoints[0]];
1218
MeshPoint& rVertex1 = _rclMesh._aclPointArray[rFace._aulPoints[1]];
1219
MeshPoint& rVertex2 = _rclMesh._aclPointArray[rFace._aulPoints[2]];
1221
auto pointIndex = [=](const Base::Vector3f& rP) {
1222
unsigned short equalP = USHRT_MAX;
1223
if (Base::Distance(rVertex0, rP) < fEps) {
1226
else if (Base::Distance(rVertex1, rP) < fEps) {
1229
else if (Base::Distance(rVertex2, rP) < fEps) {
1235
unsigned short equalP1 = pointIndex(rP1);
1236
unsigned short equalP2 = pointIndex(rP2);
1238
// both points are coincident with the corner points
1239
if (equalP1 != USHRT_MAX && equalP2 != USHRT_MAX) {
1240
return; // must not split the facet
1243
if (equalP1 != USHRT_MAX) {
1244
// get the edge to the second given point and perform a split edge operation
1245
SplitFacetOnOneEdge(ulFacetPos, rP2);
1247
else if (equalP2 != USHRT_MAX) {
1248
// get the edge to the first given point and perform a split edge operation
1249
SplitFacetOnOneEdge(ulFacetPos, rP1);
1252
SplitFacetOnTwoEdges(ulFacetPos, rP1, rP2);
1256
void MeshTopoAlgorithm::SplitFacetOnOneEdge(FacetIndex ulFacetPos, const Base::Vector3f& rP)
1258
float fMinDist = FLOAT_MAX;
1259
unsigned short iEdgeNo = USHRT_MAX;
1260
MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos];
1262
for (unsigned short i = 0; i < 3; i++) {
1263
Base::Vector3f cBase(_rclMesh._aclPointArray[rFace._aulPoints[i]]);
1264
Base::Vector3f cEnd(_rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]]);
1265
Base::Vector3f cDir = cEnd - cBase;
1267
float fDist = rP.DistanceToLine(cBase, cDir);
1268
if (fDist < fMinDist) {
1274
if (fMinDist < 0.05f) {
1275
if (rFace._aulNeighbours[iEdgeNo] != FACET_INDEX_MAX) {
1276
SplitEdge(ulFacetPos, rFace._aulNeighbours[iEdgeNo], rP);
1279
SplitOpenEdge(ulFacetPos, iEdgeNo, rP);
1284
void MeshTopoAlgorithm::SplitFacetOnTwoEdges(FacetIndex ulFacetPos,
1285
const Base::Vector3f& rP1,
1286
const Base::Vector3f& rP2)
1288
// search for the matching edges
1289
unsigned short iEdgeNo1 = USHRT_MAX, iEdgeNo2 = USHRT_MAX;
1290
float fMinDist1 = FLOAT_MAX, fMinDist2 = FLOAT_MAX;
1291
MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos];
1293
for (unsigned short i = 0; i < 3; i++) {
1294
Base::Vector3f cBase(_rclMesh._aclPointArray[rFace._aulPoints[i]]);
1295
Base::Vector3f cEnd(_rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]]);
1296
Base::Vector3f cDir = cEnd - cBase;
1298
float fDist = rP1.DistanceToLine(cBase, cDir);
1299
if (fDist < fMinDist1) {
1303
fDist = rP2.DistanceToLine(cBase, cDir);
1304
if (fDist < fMinDist2) {
1310
if (iEdgeNo1 == iEdgeNo2 || fMinDist1 >= 0.05f || fMinDist2 >= 0.05f) {
1311
return; // no valid configuration
1314
// make first point lying on the previous edge
1315
Base::Vector3f cP1 = rP1;
1316
Base::Vector3f cP2 = rP2;
1317
if ((iEdgeNo2 + 1) % 3 == iEdgeNo1) {
1318
std::swap(iEdgeNo1, iEdgeNo2);
1319
std::swap(cP1, cP2);
1322
// insert new points
1323
PointIndex cntPts1 = this->GetOrAddIndex(cP1);
1324
PointIndex cntPts2 = this->GetOrAddIndex(cP2);
1325
FacetIndex cntFts = _rclMesh.CountFacets();
1327
unsigned short v0 = (iEdgeNo2 + 1) % 3;
1328
unsigned short v1 = iEdgeNo1;
1329
unsigned short v2 = iEdgeNo2;
1331
PointIndex p0 = rFace._aulPoints[v0];
1332
PointIndex p1 = rFace._aulPoints[v1];
1333
PointIndex p2 = rFace._aulPoints[v2];
1335
FacetIndex n0 = rFace._aulNeighbours[v0];
1336
FacetIndex n1 = rFace._aulNeighbours[v1];
1337
FacetIndex n2 = rFace._aulNeighbours[v2];
1339
// Modify and add facets
1341
rFace._aulPoints[v0] = cntPts2;
1342
rFace._aulPoints[v1] = cntPts1;
1343
rFace._aulNeighbours[v0] = cntFts + 1;
1345
float dist1 = Base::DistanceP2(_rclMesh._aclPointArray[p0], cP1);
1346
float dist2 = Base::DistanceP2(_rclMesh._aclPointArray[p1], cP2);
1348
if (dist1 > dist2) {
1349
AddFacet(p0, p1, cntPts2, n0, cntFts + 1, n2);
1350
AddFacet(p1, cntPts1, cntPts2, n1, ulFacetPos, cntFts);
1353
AddFacet(p0, p1, cntPts1, n0, n1, cntFts + 1);
1354
AddFacet(p0, cntPts1, cntPts2, cntFts, ulFacetPos, n2);
1357
std::vector<FacetIndex> fixIndices;
1358
fixIndices.push_back(ulFacetPos);
1360
if (n0 != FACET_INDEX_MAX) {
1361
fixIndices.push_back(n0);
1364
// split up the neighbour facets
1365
if (n1 != FACET_INDEX_MAX) {
1366
fixIndices.push_back(n1);
1367
MeshFacet& rN = _rclMesh._aclFacetArray[n1];
1368
for (FacetIndex nbIndex : rN._aulNeighbours) {
1369
fixIndices.push_back(nbIndex);
1371
SplitFacet(n1, p1, p2, cntPts1);
1374
if (n2 != FACET_INDEX_MAX) {
1375
fixIndices.push_back(n2);
1376
MeshFacet& rN = _rclMesh._aclFacetArray[n2];
1377
for (FacetIndex nbIndex : rN._aulNeighbours) {
1378
fixIndices.push_back(nbIndex);
1380
SplitFacet(n2, p2, p0, cntPts2);
1383
FacetIndex cntFts2 = _rclMesh.CountFacets();
1384
for (FacetIndex i = cntFts; i < cntFts2; i++) {
1385
fixIndices.push_back(i);
1388
std::sort(fixIndices.begin(), fixIndices.end());
1389
fixIndices.erase(std::unique(fixIndices.begin(), fixIndices.end()), fixIndices.end());
1390
HarmonizeNeighbours(fixIndices);
1393
void MeshTopoAlgorithm::SplitFacet(FacetIndex ulFacetPos,
1398
MeshFacet& rFace = _rclMesh._aclFacetArray[ulFacetPos];
1399
unsigned short side = rFace.Side(P1, P2);
1400
if (side != USHRT_MAX) {
1401
PointIndex V1 = rFace._aulPoints[(side + 1) % 3];
1402
PointIndex V2 = rFace._aulPoints[(side + 2) % 3];
1403
FacetIndex size = _rclMesh._aclFacetArray.size();
1405
rFace._aulPoints[(side + 1) % 3] = Pn;
1406
FacetIndex N1 = rFace._aulNeighbours[(side + 1) % 3];
1407
if (N1 != FACET_INDEX_MAX) {
1408
_rclMesh._aclFacetArray[N1].ReplaceNeighbour(ulFacetPos, size);
1411
rFace._aulNeighbours[(side + 1) % 3] = ulFacetPos;
1412
AddFacet(Pn, V1, V2);
1416
void MeshTopoAlgorithm::AddFacet(PointIndex P1, PointIndex P2, PointIndex P3)
1419
facet._aulPoints[0] = P1;
1420
facet._aulPoints[1] = P2;
1421
facet._aulPoints[2] = P3;
1423
_rclMesh._aclFacetArray.push_back(facet);
1426
void MeshTopoAlgorithm::AddFacet(PointIndex P1,
1434
facet._aulPoints[0] = P1;
1435
facet._aulPoints[1] = P2;
1436
facet._aulPoints[2] = P3;
1437
facet._aulNeighbours[0] = N1;
1438
facet._aulNeighbours[1] = N2;
1439
facet._aulNeighbours[2] = N3;
1441
_rclMesh._aclFacetArray.push_back(facet);
1444
void MeshTopoAlgorithm::HarmonizeNeighbours(const std::vector<FacetIndex>& ulFacets)
1446
for (FacetIndex it : ulFacets) {
1447
for (FacetIndex jt : ulFacets) {
1448
HarmonizeNeighbours(it, jt);
1453
void MeshTopoAlgorithm::HarmonizeNeighbours(FacetIndex facet1, FacetIndex facet2)
1455
if (facet1 == facet2) {
1459
MeshFacet& rFace1 = _rclMesh._aclFacetArray[facet1];
1460
MeshFacet& rFace2 = _rclMesh._aclFacetArray[facet2];
1462
unsigned short side = rFace1.Side(rFace2);
1463
if (side != USHRT_MAX) {
1464
rFace1._aulNeighbours[side] = facet2;
1467
side = rFace2.Side(rFace1);
1468
if (side != USHRT_MAX) {
1469
rFace2._aulNeighbours[side] = facet1;
1473
void MeshTopoAlgorithm::SplitNeighbourFacet(FacetIndex ulFacetPos,
1474
unsigned short uFSide,
1475
const Base::Vector3f rPoint)
1477
MeshFacet& rclF = _rclMesh._aclFacetArray[ulFacetPos];
1479
FacetIndex ulNeighbour = rclF._aulNeighbours[uFSide];
1480
MeshFacet& rclN = _rclMesh._aclFacetArray[ulNeighbour];
1482
unsigned short uNSide = rclN.Side(rclF);
1484
PointIndex uPtInd = this->GetOrAddIndex(rPoint);
1485
FacetIndex ulSize = _rclMesh._aclFacetArray.size();
1487
// adjust the neighbourhood
1488
if (rclN._aulNeighbours[(uNSide + 1) % 3] != FACET_INDEX_MAX) {
1489
_rclMesh._aclFacetArray[rclN._aulNeighbours[(uNSide + 1) % 3]].ReplaceNeighbour(ulNeighbour,
1494
cNew._aulPoints[0] = uPtInd;
1495
cNew._aulPoints[1] = rclN._aulPoints[(uNSide + 1) % 3];
1496
cNew._aulPoints[2] = rclN._aulPoints[(uNSide + 2) % 3];
1497
cNew._aulNeighbours[0] = ulFacetPos;
1498
cNew._aulNeighbours[1] = rclN._aulNeighbours[(uNSide + 1) % 3];
1499
cNew._aulNeighbours[2] = ulNeighbour;
1502
rclN._aulPoints[(uNSide + 1) % 3] = uPtInd;
1503
rclN._aulNeighbours[(uNSide + 1) % 3] = ulSize;
1506
_rclMesh._aclFacetArray.push_back(cNew);
1510
// create 3 new facets
1511
MeshGeomFacet clFacet;
1513
// facet [P1, Ei+1, P2]
1514
clFacet._aclPoints[0] = cP1;
1515
clFacet._aclPoints[1] = _rclMesh._aclPointArray[rFace._aulPoints[(iEdgeNo1+1)%3]];
1516
clFacet._aclPoints[2] = cP2;
1517
clFacet.CalcNormal();
1518
_aclNewFacets.push_back(clFacet);
1519
// facet [P2, Ei+2, Ei]
1520
clFacet._aclPoints[0] = cP2;
1521
clFacet._aclPoints[1] = _rclMesh._aclPointArray[rFace._aulPoints[(iEdgeNo1+2)%3]];
1522
clFacet._aclPoints[2] = _rclMesh._aclPointArray[rFace._aulPoints[iEdgeNo1]];
1523
clFacet.CalcNormal();
1524
_aclNewFacets.push_back(clFacet);
1525
// facet [P2, Ei, P1]
1526
clFacet._aclPoints[0] = cP2;
1527
clFacet._aclPoints[1] = _rclMesh._aclPointArray[rFace._aulPoints[iEdgeNo1]];
1528
clFacet._aclPoints[2] = cP1;
1529
clFacet.CalcNormal();
1530
_aclNewFacets.push_back(clFacet);
1533
bool MeshTopoAlgorithm::RemoveDegeneratedFacet(FacetIndex index)
1535
if (index >= _rclMesh._aclFacetArray.size()) {
1538
MeshFacet& rFace = _rclMesh._aclFacetArray[index];
1540
// coincident corners (either topological or geometrical)
1541
for (int i = 0; i < 3; i++) {
1542
const MeshPoint& rE0 = _rclMesh._aclPointArray[rFace._aulPoints[i]];
1543
const MeshPoint& rE1 = _rclMesh._aclPointArray[rFace._aulPoints[(i + 1) % 3]];
1545
FacetIndex uN1 = rFace._aulNeighbours[(i + 1) % 3];
1546
FacetIndex uN2 = rFace._aulNeighbours[(i + 2) % 3];
1547
if (uN2 != FACET_INDEX_MAX) {
1548
_rclMesh._aclFacetArray[uN2].ReplaceNeighbour(index, uN1);
1550
if (uN1 != FACET_INDEX_MAX) {
1551
_rclMesh._aclFacetArray[uN1].ReplaceNeighbour(index, uN2);
1554
// isolate the face and remove it
1555
rFace._aulNeighbours[0] = FACET_INDEX_MAX;
1556
rFace._aulNeighbours[1] = FACET_INDEX_MAX;
1557
rFace._aulNeighbours[2] = FACET_INDEX_MAX;
1558
_rclMesh.DeleteFacet(index);
1563
// We have a facet of the form
1564
// P0 +----+------+P2
1566
for (int j = 0; j < 3; j++) {
1567
Base::Vector3f cVec1 = _rclMesh._aclPointArray[rFace._aulPoints[(j + 1) % 3]]
1568
- _rclMesh._aclPointArray[rFace._aulPoints[j]];
1569
Base::Vector3f cVec2 = _rclMesh._aclPointArray[rFace._aulPoints[(j + 2) % 3]]
1570
- _rclMesh._aclPointArray[rFace._aulPoints[j]];
1572
// adjust the neighbourhoods and point indices
1573
if (cVec1 * cVec2 < 0.0f) {
1574
FacetIndex uN1 = rFace._aulNeighbours[(j + 1) % 3];
1575
if (uN1 != FACET_INDEX_MAX) {
1576
// get the neighbour and common edge side
1577
MeshFacet& rNb = _rclMesh._aclFacetArray[uN1];
1578
unsigned short side = rNb.Side(index);
1580
// bend the point indices
1581
rFace._aulPoints[(j + 2) % 3] = rNb._aulPoints[(side + 2) % 3];
1582
rNb._aulPoints[(side + 1) % 3] = rFace._aulPoints[j];
1584
// set correct neighbourhood
1585
FacetIndex uN2 = rFace._aulNeighbours[(j + 2) % 3];
1586
rNb._aulNeighbours[side] = uN2;
1587
if (uN2 != FACET_INDEX_MAX) {
1588
_rclMesh._aclFacetArray[uN2].ReplaceNeighbour(index, uN1);
1590
FacetIndex uN3 = rNb._aulNeighbours[(side + 1) % 3];
1591
rFace._aulNeighbours[(j + 1) % 3] = uN3;
1592
if (uN3 != FACET_INDEX_MAX) {
1593
_rclMesh._aclFacetArray[uN3].ReplaceNeighbour(uN1, index);
1595
rNb._aulNeighbours[(side + 1) % 3] = index;
1596
rFace._aulNeighbours[(j + 2) % 3] = uN1;
1599
_rclMesh.DeleteFacet(index);
1609
bool MeshTopoAlgorithm::RemoveCorruptedFacet(FacetIndex index)
1611
if (index >= _rclMesh._aclFacetArray.size()) {
1614
MeshFacet& rFace = _rclMesh._aclFacetArray[index];
1616
// coincident corners (topological)
1617
for (int i = 0; i < 3; i++) {
1618
if (rFace._aulPoints[i] == rFace._aulPoints[(i + 1) % 3]) {
1619
FacetIndex uN1 = rFace._aulNeighbours[(i + 1) % 3];
1620
FacetIndex uN2 = rFace._aulNeighbours[(i + 2) % 3];
1621
if (uN2 != FACET_INDEX_MAX) {
1622
_rclMesh._aclFacetArray[uN2].ReplaceNeighbour(index, uN1);
1624
if (uN1 != FACET_INDEX_MAX) {
1625
_rclMesh._aclFacetArray[uN1].ReplaceNeighbour(index, uN2);
1628
// isolate the face and remove it
1629
rFace._aulNeighbours[0] = FACET_INDEX_MAX;
1630
rFace._aulNeighbours[1] = FACET_INDEX_MAX;
1631
rFace._aulNeighbours[2] = FACET_INDEX_MAX;
1632
_rclMesh.DeleteFacet(index);
1640
void MeshTopoAlgorithm::FillupHoles(unsigned long length,
1642
AbstractPolygonTriangulator& cTria,
1643
std::list<std::vector<PointIndex>>& aFailed)
1645
// get the mesh boundaries as an array of point indices
1646
std::list<std::vector<PointIndex>> aBorders, aFillBorders;
1647
MeshAlgorithm cAlgo(_rclMesh);
1648
cAlgo.GetMeshBorders(aBorders);
1650
// split boundary loops if needed
1651
cAlgo.SplitBoundaryLoops(aBorders);
1653
for (const auto& aBorder : aBorders) {
1654
if (aBorder.size() - 1 <= length) { // ignore boundary with too many edges
1655
aFillBorders.push_back(aBorder);
1659
if (!aFillBorders.empty()) {
1660
FillupHoles(level, cTria, aFillBorders, aFailed);
1664
void MeshTopoAlgorithm::FillupHoles(int level,
1665
AbstractPolygonTriangulator& cTria,
1666
const std::list<std::vector<PointIndex>>& aBorders,
1667
std::list<std::vector<PointIndex>>& aFailed)
1669
// get the facets to a point
1670
MeshRefPointToFacets cPt2Fac(_rclMesh);
1671
MeshAlgorithm cAlgo(_rclMesh);
1673
MeshFacetArray newFacets;
1674
MeshPointArray newPoints;
1675
unsigned long numberOfOldPoints = _rclMesh._aclPointArray.size();
1676
for (const auto& aBorder : aBorders) {
1677
MeshFacetArray cFacets;
1678
MeshPointArray cPoints;
1679
std::vector<PointIndex> bound = aBorder;
1680
if (cAlgo.FillupHole(bound, cTria, cFacets, cPoints, level, &cPt2Fac)) {
1681
if (bound.front() == bound.back()) {
1684
// the triangulation may produce additional points which we must take into account when
1685
// appending to the mesh
1686
if (cPoints.size() > bound.size()) {
1687
unsigned long countBoundaryPoints = bound.size();
1688
unsigned long countDifference = cPoints.size() - countBoundaryPoints;
1689
MeshPointArray::_TIterator pt = cPoints.begin() + countBoundaryPoints;
1690
for (unsigned long i = 0; i < countDifference; i++, pt++) {
1691
bound.push_back(numberOfOldPoints++);
1692
newPoints.push_back(*pt);
1695
if (cTria.NeedsReindexing()) {
1696
for (auto& cFacet : cFacets) {
1697
cFacet._aulPoints[0] = bound[cFacet._aulPoints[0]];
1698
cFacet._aulPoints[1] = bound[cFacet._aulPoints[1]];
1699
cFacet._aulPoints[2] = bound[cFacet._aulPoints[2]];
1700
newFacets.push_back(cFacet);
1704
for (auto& cFacet : cFacets) {
1705
newFacets.push_back(cFacet);
1710
aFailed.push_back(aBorder);
1714
// insert new points and faces into the mesh structure
1715
_rclMesh._aclPointArray.insert(_rclMesh._aclPointArray.end(),
1718
for (const auto& newPoint : newPoints) {
1719
_rclMesh._clBoundBox.Add(newPoint);
1721
if (!newFacets.empty()) {
1722
// Do some checks for invalid point indices
1723
MeshFacetArray addFacets;
1724
addFacets.reserve(newFacets.size());
1725
unsigned long ctPoints = _rclMesh.CountPoints();
1726
for (auto& newFacet : newFacets) {
1727
if (newFacet._aulPoints[0] >= ctPoints || newFacet._aulPoints[1] >= ctPoints
1728
|| newFacet._aulPoints[2] >= ctPoints) {
1729
Base::Console().Log("Ignore invalid face <%d, %d, %d> (%d vertices)\n",
1730
newFacet._aulPoints[0],
1731
newFacet._aulPoints[1],
1732
newFacet._aulPoints[2],
1736
addFacets.push_back(newFacet);
1739
_rclMesh.AddFacets(addFacets, true);
1743
void MeshTopoAlgorithm::FindHoles(unsigned long length,
1744
std::list<std::vector<PointIndex>>& aBorders) const
1746
std::list<std::vector<PointIndex>> border;
1747
MeshAlgorithm cAlgo(_rclMesh);
1748
cAlgo.GetMeshBorders(border);
1749
for (const auto& it : border) {
1750
if (it.size() <= length) {
1751
aBorders.push_back(it);
1756
void MeshTopoAlgorithm::FindComponents(unsigned long count, std::vector<FacetIndex>& findIndices)
1758
std::vector<std::vector<FacetIndex>> segments;
1759
MeshComponents comp(_rclMesh);
1760
comp.SearchForComponents(MeshComponents::OverEdge, segments);
1762
for (const auto& segment : segments) {
1763
if (segment.size() <= count) {
1764
findIndices.insert(findIndices.end(), segment.begin(), segment.end());
1769
void MeshTopoAlgorithm::RemoveComponents(unsigned long count)
1771
std::vector<FacetIndex> removeFacets;
1772
FindComponents(count, removeFacets);
1773
if (!removeFacets.empty()) {
1774
_rclMesh.DeleteFacets(removeFacets);
1778
void MeshTopoAlgorithm::HarmonizeNormals()
1780
std::vector<FacetIndex> uIndices = MeshEvalOrientation(_rclMesh).GetIndices();
1781
for (FacetIndex index : uIndices) {
1782
_rclMesh._aclFacetArray[index].FlipNormal();
1786
void MeshTopoAlgorithm::FlipNormals()
1788
for (MeshFacetArray::_TIterator i = _rclMesh._aclFacetArray.begin();
1789
i < _rclMesh._aclFacetArray.end();
1795
// ---------------------------------------------------------------------------
1798
* Some important formulas:
1800
* Ne = 3Nv - Nb + 3B + 6(G-R)
1801
* Nt = 2Nv - Nb + 2B + 4(G-R)
1803
* Ne <= 3Nv + 6(G-R)
1804
* Nt <= 2Nv + 4(G-R)
1806
* Ne ~ 3Nv, Nv >> G, Nv >> R
1807
* Nt ~ 2Nv, Nv >> G, Nv >> R
1812
* Nb = #Boundary vertices
1814
* G = Genus (Number of holes)
1817
* See also http://max-limper.de/publications/Euler/
1820
MeshComponents::MeshComponents(const MeshKernel& rclMesh)
1824
void MeshComponents::SearchForComponents(TMode tMode,
1825
std::vector<std::vector<FacetIndex>>& aclT) const
1828
std::vector<FacetIndex> aulAllFacets(_rclMesh.CountFacets());
1830
for (FacetIndex& aulAllFacet : aulAllFacets) {
1834
SearchForComponents(tMode, aulAllFacets, aclT);
1837
void MeshComponents::SearchForComponents(TMode tMode,
1838
const std::vector<FacetIndex>& aSegment,
1839
std::vector<std::vector<FacetIndex>>& aclT) const
1841
FacetIndex ulStartFacet {};
1843
if (_rclMesh.CountFacets() == 0) {
1847
// reset VISIT flags
1848
MeshAlgorithm cAlgo(_rclMesh);
1849
cAlgo.SetFacetFlag(MeshFacet::VISIT);
1850
cAlgo.ResetFacetsFlag(aSegment, MeshFacet::VISIT);
1852
const MeshFacetArray& rFAry = _rclMesh.GetFacets();
1853
MeshFacetArray::_TConstIterator iTri = rFAry.begin();
1854
MeshFacetArray::_TConstIterator iBeg = rFAry.begin();
1855
MeshFacetArray::_TConstIterator iEnd = rFAry.end();
1857
// start from the first not visited facet
1858
unsigned long ulVisited = cAlgo.CountFacetFlag(MeshFacet::VISIT);
1859
MeshIsNotFlag<MeshFacet> flag;
1860
iTri = std::find_if(iTri, iEnd, [flag](const MeshFacet& f) {
1861
return flag(f, MeshFacet::VISIT);
1863
ulStartFacet = iTri - iBeg;
1866
std::vector<FacetIndex> aclComponent;
1867
std::vector<std::vector<FacetIndex>> aclConnectComp;
1868
MeshTopFacetVisitor clFVisitor(aclComponent);
1870
while (ulStartFacet != FACET_INDEX_MAX) {
1871
// collect all facets of a component
1872
aclComponent.clear();
1873
if (tMode == OverEdge) {
1874
ulVisited += _rclMesh.VisitNeighbourFacets(clFVisitor, ulStartFacet);
1876
else if (tMode == OverPoint) {
1877
ulVisited += _rclMesh.VisitNeighbourFacetsOverCorners(clFVisitor, ulStartFacet);
1880
// get also start facet
1881
aclComponent.push_back(ulStartFacet);
1882
aclConnectComp.push_back(aclComponent);
1884
// if the mesh consists of several topologic independent components
1885
// We can search from position 'iTri' on because all elements _before_ are already visited
1886
// what we know from the previous iteration.
1887
iTri = std::find_if(iTri, iEnd, [flag](const MeshFacet& f) {
1888
return flag(f, MeshFacet::VISIT);
1892
ulStartFacet = iTri - iBeg;
1895
ulStartFacet = FACET_INDEX_MAX;
1899
// sort components by size (descending order)
1900
std::sort(aclConnectComp.begin(), aclConnectComp.end(), CNofFacetsCompare());
1901
aclT = aclConnectComp;
1902
boost::ignore_unused(ulVisited);