1
/***************************************************************************
2
* Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net> *
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"
28
#include <Base/Console.h>
29
#include <Base/Exception.h>
30
#include <Mod/Mesh/App/WildMagic4/Wm4Delaunay2.h>
32
#include "Approximation.h"
33
#include "MeshKernel.h"
34
#include "Triangulation.h"
37
using namespace MeshCore;
40
bool TriangulationVerifier::Accept(const Base::Vector3f& n,
41
const Base::Vector3f& p1,
42
const Base::Vector3f& p2,
43
const Base::Vector3f& p3) const
45
float ref_dist = (p2 - p1) * n;
46
float tri_dist = (p3 - p1) * n;
47
return (ref_dist * tri_dist <= 0.0f);
50
bool TriangulationVerifier::MustFlip(const Base::Vector3f& n1, const Base::Vector3f& n2) const
52
return n1.Dot(n2) <= 0.0f;
55
bool TriangulationVerifierV2::Accept(const Base::Vector3f& n,
56
const Base::Vector3f& p1,
57
const Base::Vector3f& p2,
58
const Base::Vector3f& p3) const
60
float ref_dist = (p2 - p1) * n;
61
float tri_dist = (p3 - p1) * n;
62
float prod = ref_dist * tri_dist;
67
bool TriangulationVerifierV2::MustFlip(const Base::Vector3f& n1, const Base::Vector3f& n2) const
69
float dot = n1.Dot(n2);
74
// ----------------------------------------------------------------------------
76
AbstractPolygonTriangulator::AbstractPolygonTriangulator()
78
, _verifier {new TriangulationVerifier()}
81
AbstractPolygonTriangulator::~AbstractPolygonTriangulator()
86
TriangulationVerifier* AbstractPolygonTriangulator::GetVerifier() const
91
void AbstractPolygonTriangulator::SetVerifier(TriangulationVerifier* v)
97
void AbstractPolygonTriangulator::SetPolygon(const std::vector<Base::Vector3f>& raclPoints)
99
this->_points = raclPoints;
100
if (!this->_points.empty()) {
101
if (this->_points.front() == this->_points.back()) {
102
this->_points.pop_back();
107
std::vector<Base::Vector3f> AbstractPolygonTriangulator::GetPolygon() const
112
float AbstractPolygonTriangulator::GetLength() const
115
if (_points.size() > 2) {
116
for (std::vector<Base::Vector3f>::const_iterator it = _points.begin(); it != _points.end();
118
std::vector<Base::Vector3f>::const_iterator jt = it + 1;
119
if (jt == _points.end()) {
120
jt = _points.begin();
122
len += Base::Distance(*it, *jt);
129
std::vector<Base::Vector3f> AbstractPolygonTriangulator::AddedPoints() const
131
// Apply the inverse transformation to project back to world coordinates
132
std::vector<Base::Vector3f> added;
133
added.reserve(_newpoints.size());
134
for (auto point : _newpoints) {
135
added.push_back(_inverse * point);
140
Base::Matrix4D AbstractPolygonTriangulator::GetTransformToFitPlane() const
143
for (auto point : _points) {
144
planeFit.AddPoint(point);
147
if (planeFit.Fit() >= FLOAT_MAX) {
148
throw Base::RuntimeError("Plane fit failed");
151
Base::Vector3f bs = planeFit.GetBase();
152
Base::Vector3f ex = planeFit.GetDirU();
153
Base::Vector3f ey = planeFit.GetDirV();
154
Base::Vector3f ez = planeFit.GetNormal();
156
// build the matrix for the inverse transformation
157
Base::Matrix4D rInverse;
158
rInverse.setToUnity();
159
rInverse[0][0] = static_cast<double>(ex.x);
160
rInverse[0][1] = static_cast<double>(ey.x);
161
rInverse[0][2] = static_cast<double>(ez.x);
162
rInverse[0][3] = static_cast<double>(bs.x);
164
rInverse[1][0] = static_cast<double>(ex.y);
165
rInverse[1][1] = static_cast<double>(ey.y);
166
rInverse[1][2] = static_cast<double>(ez.y);
167
rInverse[1][3] = static_cast<double>(bs.y);
169
rInverse[2][0] = static_cast<double>(ex.z);
170
rInverse[2][1] = static_cast<double>(ey.z);
171
rInverse[2][2] = static_cast<double>(ez.z);
172
rInverse[2][3] = static_cast<double>(bs.z);
177
std::vector<Base::Vector3f> AbstractPolygonTriangulator::ProjectToFitPlane()
179
std::vector<Base::Vector3f> proj = _points;
180
_inverse = GetTransformToFitPlane();
181
Base::Vector3f bs(static_cast<float>(_inverse[0][3]),
182
static_cast<float>(_inverse[1][3]),
183
static_cast<float>(_inverse[2][3]));
184
Base::Vector3f ex(static_cast<float>(_inverse[0][0]),
185
static_cast<float>(_inverse[1][0]),
186
static_cast<float>(_inverse[2][0]));
187
Base::Vector3f ey(static_cast<float>(_inverse[0][1]),
188
static_cast<float>(_inverse[1][1]),
189
static_cast<float>(_inverse[2][1]));
190
for (auto& jt : proj) {
191
jt.TransformToCoordinateSystem(bs, ex, ey);
196
void AbstractPolygonTriangulator::PostProcessing(const std::vector<Base::Vector3f>& points)
198
// For a good approximation we should have enough points, i.e. for 9 parameters
199
// for the fit function we should have at least 50 points.
200
unsigned int uMinPts = 50;
202
PolynomialFit polyFit;
203
Base::Vector3f bs(static_cast<float>(_inverse[0][3]),
204
static_cast<float>(_inverse[1][3]),
205
static_cast<float>(_inverse[2][3]));
206
Base::Vector3f ex(static_cast<float>(_inverse[0][0]),
207
static_cast<float>(_inverse[1][0]),
208
static_cast<float>(_inverse[2][0]));
209
Base::Vector3f ey(static_cast<float>(_inverse[0][1]),
210
static_cast<float>(_inverse[1][1]),
211
static_cast<float>(_inverse[2][1]));
213
for (auto pt : points) {
214
pt.TransformToCoordinateSystem(bs, ex, ey);
215
polyFit.AddPoint(pt);
218
if (polyFit.CountPoints() >= uMinPts && polyFit.Fit() < FLOAT_MAX) {
219
for (auto& newpoint : _newpoints) {
220
newpoint.z = static_cast<float>(polyFit.Value(newpoint.x, newpoint.y));
225
MeshGeomFacet AbstractPolygonTriangulator::GetTriangle(const MeshPointArray& points,
226
const MeshFacet& facet) const
228
MeshGeomFacet triangle;
229
triangle._aclPoints[0] = points[facet._aulPoints[0]];
230
triangle._aclPoints[1] = points[facet._aulPoints[1]];
231
triangle._aclPoints[2] = points[facet._aulPoints[2]];
235
bool AbstractPolygonTriangulator::TriangulatePolygon()
238
if (!this->_indices.empty() && this->_points.size() != this->_indices.size()) {
239
Base::Console().Log("Triangulation: %d points <> %d indices\n",
244
bool ok = Triangulate();
250
catch (const Base::Exception& e) {
251
Base::Console().Log("Triangulation: %s\n", e.what());
254
catch (const std::exception& e) {
255
Base::Console().Log("Triangulation: %s\n", e.what());
263
std::vector<PointIndex> AbstractPolygonTriangulator::GetInfo() const
268
void AbstractPolygonTriangulator::Discard()
276
void AbstractPolygonTriangulator::Reset()
279
void AbstractPolygonTriangulator::Done()
281
_info.push_back(_points.size());
285
// -------------------------------------------------------------
287
EarClippingTriangulator::EarClippingTriangulator() = default;
289
bool EarClippingTriangulator::Triangulate()
294
std::vector<Base::Vector3f> pts = ProjectToFitPlane();
295
std::vector<PointIndex> result;
297
// Invoke the triangulator to triangulate this polygon.
298
Triangulate::Process(pts, result);
300
// print out the results.
301
size_t tcount = result.size() / 3;
303
bool ok = tcount + 2 == _points.size();
304
if (tcount > _points.size()) {
305
return false; // no valid triangulation
308
MeshGeomFacet clFacet;
309
MeshFacet clTopFacet;
310
for (size_t i = 0; i < tcount; i++) {
311
if (Triangulate::_invert) {
312
clFacet._aclPoints[0] = _points[result[i * 3 + 0]];
313
clFacet._aclPoints[2] = _points[result[i * 3 + 1]];
314
clFacet._aclPoints[1] = _points[result[i * 3 + 2]];
315
clTopFacet._aulPoints[0] = result[i * 3 + 0];
316
clTopFacet._aulPoints[2] = result[i * 3 + 1];
317
clTopFacet._aulPoints[1] = result[i * 3 + 2];
320
clFacet._aclPoints[0] = _points[result[i * 3 + 0]];
321
clFacet._aclPoints[1] = _points[result[i * 3 + 1]];
322
clFacet._aclPoints[2] = _points[result[i * 3 + 2]];
323
clTopFacet._aulPoints[0] = result[i * 3 + 0];
324
clTopFacet._aulPoints[1] = result[i * 3 + 1];
325
clTopFacet._aulPoints[2] = result[i * 3 + 2];
328
_triangles.push_back(clFacet);
329
_facets.push_back(clTopFacet);
335
float EarClippingTriangulator::Triangulate::Area(const std::vector<Base::Vector3f>& contour)
337
int n = contour.size();
341
for (int p = n - 1, q = 0; q < n; p = q++) {
342
A += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
348
InsideTriangle decides if a point P is Inside of the triangle
351
bool EarClippingTriangulator::Triangulate::InsideTriangle(float Ax,
360
float ax {}, ay {}, bx {}, by {}, cx {}, cy {}, apx {}, apy {}, bpx {}, bpy {}, cpx {}, cpy {};
361
float cCROSSap {}, bCROSScp {}, aCROSSbp {};
376
aCROSSbp = ax * bpy - ay * bpx;
377
cCROSSap = cx * apy - cy * apx;
378
bCROSScp = bx * cpy - by * cpx;
380
return ((aCROSSbp >= FLOAT_EPS) && (bCROSScp >= FLOAT_EPS) && (cCROSSap >= FLOAT_EPS));
383
bool EarClippingTriangulator::Triangulate::Snip(const std::vector<Base::Vector3f>& contour,
391
float Ax {}, Ay {}, Bx {}, By {}, Cx {}, Cy {}, Px {}, Py {};
393
Ax = contour[V[u]].x;
394
Ay = contour[V[u]].y;
396
Bx = contour[V[v]].x;
397
By = contour[V[v]].y;
399
Cx = contour[V[w]].x;
400
Cy = contour[V[w]].y;
402
if (FLOAT_EPS > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) {
406
for (p = 0; p < n; p++) {
407
if ((p == u) || (p == v) || (p == w)) {
410
Px = contour[V[p]].x;
411
Py = contour[V[p]].y;
412
if (InsideTriangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py)) {
420
bool EarClippingTriangulator::Triangulate::_invert = false;
422
bool EarClippingTriangulator::Triangulate::Process(const std::vector<Base::Vector3f>& contour,
423
std::vector<PointIndex>& result)
425
/* allocate and initialize list of Vertices in polygon */
427
int n = contour.size();
434
/* we want a counter-clockwise polygon in V */
436
if (0.0f < Area(contour)) {
437
for (int v = 0; v < n; v++) {
442
// for(int v=0; v<n; v++) V[v] = (n-1)-v;
444
for (int v = 0; v < n; v++) {
452
/* remove nv-2 Vertices, creating 1 triangle every time */
453
int count = 2 * nv; /* error detection */
455
for (int v = nv - 1; nv > 2;) {
456
/* if we loop, it is probably a non-simple polygon */
457
if (0 >= (count--)) {
458
//** Triangulate: ERROR - probable bad polygon!
463
/* three consecutive vertices in current polygon, <u,v,w> */
466
u = 0; /* previous */
477
if (Snip(contour, u, v, w, nv, V)) {
478
int a {}, b {}, c {}, s {}, t {};
480
/* true names of the vertices */
485
/* output Triangle */
490
/* remove v from remaining polygon */
491
for (s = v, t = v + 1; t < nv; s++, t++) {
497
/* reset error detection counter */
507
// -------------------------------------------------------------
509
QuasiDelaunayTriangulator::QuasiDelaunayTriangulator() = default;
511
bool QuasiDelaunayTriangulator::Triangulate()
513
if (!EarClippingTriangulator::Triangulate()) {
514
return false; // no valid triangulation
517
// For each internal edge get the adjacent facets. When doing an edge swap we must update
519
std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>> aEdge2Face;
520
for (std::vector<MeshFacet>::iterator pI = _facets.begin(); pI != _facets.end(); ++pI) {
521
for (int i = 0; i < 3; i++) {
522
PointIndex ulPt0 = std::min<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]);
523
PointIndex ulPt1 = std::max<PointIndex>(pI->_aulPoints[i], pI->_aulPoints[(i + 1) % 3]);
524
// ignore borderlines of the polygon
525
if ((ulPt1 - ulPt0) % (_points.size() - 1) > 1) {
526
aEdge2Face[std::pair<PointIndex, PointIndex>(ulPt0, ulPt1)].push_back(
527
pI - _facets.begin());
532
// fill up this list with all internal edges and perform swap edges until this list is empty
533
std::list<std::pair<PointIndex, PointIndex>> aEdgeList;
534
std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>>::iterator pE;
535
for (pE = aEdge2Face.begin(); pE != aEdge2Face.end(); ++pE) {
536
aEdgeList.push_back(pE->first);
539
// to be sure to avoid an endless loop
540
size_t uMaxIter = 5 * aEdge2Face.size();
542
// Perform a swap edge where needed
543
while (!aEdgeList.empty() && uMaxIter > 0) {
544
// get the first edge and remove it from the list
545
std::pair<PointIndex, PointIndex> aEdge = aEdgeList.front();
546
aEdgeList.pop_front();
549
// get the adjacent facets to this edge
550
pE = aEdge2Face.find(aEdge);
552
// this edge has been removed some iterations before
553
if (pE == aEdge2Face.end()) {
557
MeshFacet& rF1 = _facets[pE->second[0]];
558
MeshFacet& rF2 = _facets[pE->second[1]];
559
unsigned short side1 = rF1.Side(aEdge.first, aEdge.second);
561
Base::Vector3f cP1 = _points[rF1._aulPoints[side1]];
562
Base::Vector3f cP2 = _points[rF1._aulPoints[(side1 + 1) % 3]];
563
Base::Vector3f cP3 = _points[rF1._aulPoints[(side1 + 2) % 3]];
565
unsigned short side2 = rF2.Side(aEdge.first, aEdge.second);
566
Base::Vector3f cP4 = _points[rF2._aulPoints[(side2 + 2) % 3]];
568
MeshGeomFacet cT1(cP1, cP2, cP3);
569
float fMax1 = cT1.MaximumAngle();
570
MeshGeomFacet cT2(cP2, cP1, cP4);
571
float fMax2 = cT2.MaximumAngle();
572
MeshGeomFacet cT3(cP4, cP3, cP1);
573
float fMax3 = cT3.MaximumAngle();
574
MeshGeomFacet cT4(cP3, cP4, cP2);
575
float fMax4 = cT4.MaximumAngle();
577
float fMax12 = std::max<float>(fMax1, fMax2);
578
float fMax34 = std::max<float>(fMax3, fMax4);
580
// We must make sure that the two adjacent triangles builds a convex polygon, otherwise
581
// the swap edge operation is illegal
582
Base::Vector3f cU = cP2 - cP1;
583
Base::Vector3f cV = cP4 - cP3;
584
// build a helper plane through cP1 that must separate cP3 and cP4
585
Base::Vector3f cN1 = (cU % cV) % cU;
586
if (((cP3 - cP1) * cN1) * ((cP4 - cP1) * cN1) >= 0.0f) {
587
continue; // not convex
589
// build a helper plane through cP3 that must separate cP1 and cP2
590
Base::Vector3f cN2 = (cU % cV) % cV;
591
if (((cP1 - cP3) * cN2) * ((cP2 - cP3) * cN2) >= 0.0f) {
592
continue; // not convex
595
// ok, here we should perform a swap edge to minimize the maximum angle
596
if (fMax12 > fMax34) {
597
rF1._aulPoints[(side1 + 1) % 3] = rF2._aulPoints[(side2 + 2) % 3];
598
rF2._aulPoints[(side2 + 1) % 3] = rF1._aulPoints[(side1 + 2) % 3];
600
// adjust the edge list
601
for (int i = 0; i < 3; i++) {
602
std::map<std::pair<PointIndex, PointIndex>, std::vector<FacetIndex>>::iterator it;
605
std::min<PointIndex>(rF1._aulPoints[i], rF1._aulPoints[(i + 1) % 3]);
607
std::max<PointIndex>(rF1._aulPoints[i], rF1._aulPoints[(i + 1) % 3]);
608
it = aEdge2Face.find(std::make_pair(ulPt0, ulPt1));
609
if (it != aEdge2Face.end()) {
610
if (it->second[0] == pE->second[1]) {
611
it->second[0] = pE->second[0];
613
else if (it->second[1] == pE->second[1]) {
614
it->second[1] = pE->second[0];
616
aEdgeList.push_back(it->first);
620
ulPt0 = std::min<PointIndex>(rF2._aulPoints[i], rF2._aulPoints[(i + 1) % 3]);
621
ulPt1 = std::max<PointIndex>(rF2._aulPoints[i], rF2._aulPoints[(i + 1) % 3]);
622
it = aEdge2Face.find(std::make_pair(ulPt0, ulPt1));
623
if (it != aEdge2Face.end()) {
624
if (it->second[0] == pE->second[0]) {
625
it->second[0] = pE->second[1];
627
else if (it->second[1] == pE->second[0]) {
628
it->second[1] = pE->second[1];
630
aEdgeList.push_back(it->first);
634
// Now we must remove the edge and replace it through the new edge
635
PointIndex ulPt0 = std::min<PointIndex>(rF1._aulPoints[(side1 + 1) % 3],
636
rF2._aulPoints[(side2 + 1) % 3]);
637
PointIndex ulPt1 = std::max<PointIndex>(rF1._aulPoints[(side1 + 1) % 3],
638
rF2._aulPoints[(side2 + 1) % 3]);
639
std::pair<PointIndex, PointIndex> aNewEdge = std::make_pair(ulPt0, ulPt1);
640
aEdge2Face[aNewEdge] = pE->second;
641
aEdge2Face.erase(pE);
648
// -------------------------------------------------------------
652
namespace Triangulation
656
bool operator()(const Base::Vector3f& p, const Base::Vector3f& q) const
658
if (fabs(p.x - q.x) < MeshDefinitions::_fMinPointDistanceD1) {
659
if (fabs(p.y - q.y) < MeshDefinitions::_fMinPointDistanceD1) {
671
struct Vertex2d_EqualTo
673
bool operator()(const Base::Vector3f& p, const Base::Vector3f& q) const
675
if (fabs(p.x - q.x) < MeshDefinitions::_fMinPointDistanceD1
676
&& fabs(p.y - q.y) < MeshDefinitions::_fMinPointDistanceD1) {
683
} // namespace Triangulation
684
} // namespace MeshCore
686
DelaunayTriangulator::DelaunayTriangulator() = default;
688
bool DelaunayTriangulator::Triangulate()
690
// before starting the triangulation we must make sure that all polygon
691
// points are different
692
std::vector<Base::Vector3f> aPoints = _points;
693
// sort the points ascending x,y coordinates
694
std::sort(aPoints.begin(), aPoints.end(), Triangulation::Vertex2d_Less());
695
// if there are two adjacent points whose distance is less then an epsilon
696
if (std::adjacent_find(aPoints.begin(), aPoints.end(), Triangulation::Vertex2d_EqualTo())
704
std::vector<Wm4::Vector2d> akVertex;
705
akVertex.reserve(_points.size());
706
for (const auto& point : _points) {
707
akVertex.emplace_back(static_cast<double>(point.x), static_cast<double>(point.y));
710
Wm4::Delaunay2d del(static_cast<int>(akVertex.size()),
714
Wm4::Query::QT_INT64);
715
int iTQuantity = del.GetSimplexQuantity();
716
std::vector<int> aiTVertex(static_cast<size_t>(3 * iTQuantity));
718
bool succeeded = false;
719
if (iTQuantity > 0) {
720
size_t uiSize = static_cast<size_t>(3 * iTQuantity) * sizeof(int);
721
Wm4::System::Memcpy(&(aiTVertex[0]), uiSize, del.GetIndices(), uiSize);
723
// If H is the number of hull edges and N is the number of vertices,
724
// then the triangulation must have 2*N-2-H triangles and 3*N-3-H
727
int* aiIndex = nullptr;
728
del.GetHull(iEQuantity, aiIndex);
729
int iUniqueVQuantity = del.GetUniqueVertexQuantity();
730
int iTVerify = 2 * iUniqueVQuantity - 2 - iEQuantity;
731
(void)iTVerify; // avoid warning in release build
732
succeeded = (iTVerify == iTQuantity);
733
int iEVerify = 3 * iUniqueVQuantity - 3 - iEQuantity;
734
(void)iEVerify; // avoid warning about unused variable
738
MeshGeomFacet triangle;
740
for (int i = 0; i < iTQuantity; i++) {
741
for (int j = 0; j < 3; j++) {
742
size_t index = static_cast<size_t>(aiTVertex[static_cast<size_t>(3 * i + j)]);
743
facet._aulPoints[j] = static_cast<PointIndex>(index);
744
triangle._aclPoints[j].x = static_cast<float>(akVertex[index].X());
745
triangle._aclPoints[j].y = static_cast<float>(akVertex[index].Y());
748
_triangles.push_back(triangle);
749
_facets.push_back(facet);
755
// -------------------------------------------------------------
757
FlatTriangulator::FlatTriangulator() = default;
759
bool FlatTriangulator::Triangulate()
762
// before starting the triangulation we must make sure that all polygon
763
// points are different
764
std::vector<Base::Vector3f> aPoints = ProjectToFitPlane();
765
std::vector<Base::Vector3f> tmp = aPoints;
766
// sort the points ascending x,y coordinates
767
std::sort(tmp.begin(), tmp.end(), Triangulation::Vertex2d_Less());
768
// if there are two adjacent points whose distance is less then an epsilon
769
if (std::adjacent_find(tmp.begin(), tmp.end(), Triangulation::Vertex2d_EqualTo()) < tmp.end()) {
776
// Todo: Implement algorithm for constraint delaunay triangulation
777
QuasiDelaunayTriangulator tria;
778
tria.SetPolygon(this->GetPolygon());
779
bool succeeded = tria.TriangulatePolygon();
780
this->_facets = tria.GetFacets();
781
this->_triangles = tria.GetTriangles();
786
void FlatTriangulator::PostProcessing(const std::vector<Base::Vector3f>&)
789
// -------------------------------------------------------------
791
ConstraintDelaunayTriangulator::ConstraintDelaunayTriangulator(float area)
794
// silent warning: -Wunused-private-field
798
bool ConstraintDelaunayTriangulator::Triangulate()
801
// before starting the triangulation we must make sure that all polygon
802
// points are different
803
std::vector<Base::Vector3f> aPoints = ProjectToFitPlane();
804
std::vector<Base::Vector3f> tmp = aPoints;
805
// sort the points ascending x,y coordinates
806
std::sort(tmp.begin(), tmp.end(), Triangulation::Vertex2d_Less());
807
// if there are two adjacent points whose distance is less then an epsilon
808
if (std::adjacent_find(tmp.begin(), tmp.end(), Triangulation::Vertex2d_EqualTo()) < tmp.end()) {
815
// Todo: Implement algorithm for constraint delaunay triangulation
816
QuasiDelaunayTriangulator tria;
817
tria.SetPolygon(this->GetPolygon());
818
bool succeeded = tria.TriangulatePolygon();
819
this->_facets = tria.GetFacets();
820
this->_triangles = tria.GetTriangles();
825
// -------------------------------------------------------------
828
Triangulator::Triangulator(const MeshKernel& k, bool flat) : _kernel(k)
832
Triangulator::~Triangulator()
836
bool Triangulator::Triangulate()
841
MeshGeomFacet Triangulator::GetTriangle(const MeshPointArray&,
842
const MeshFacet& facet) const
844
return MeshGeomFacet();
847
void Triangulator::PostProcessing(const std::vector<Base::Vector3f>&)
851
void Triangulator::Discard()
853
AbstractPolygonTriangulator::Discard();
856
void Triangulator::Reset()