xmsextractor  1.0
XmUGridTriangles2d.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
6 //------------------------------------------------------------------------------
7 
8 //----- Included files ---------------------------------------------------------
9 
10 // 1. Precompiled header
11 
12 // 2. My own header
14 
15 // 3. Standard library headers
16 #include <sstream>
17 #include <cmath>
18 
19 // 4. External library headers
20 #include <boost/container/flat_map.hpp>
21 
22 // 5. Shared code headers
23 #include <xmscore/misc/XmError.h>
24 #include <xmscore/misc/XmLog.h>
25 #include <xmsgrid/geometry/geoms.h>
26 #include <xmsgrid/geometry/GmTriSearch.h>
27 #include <xmsgrid/ugrid/XmUGrid.h>
28 
29 // 6. Non-shared code headers
30 
31 //----- Forward declarations ---------------------------------------------------
32 
33 //----- External globals -------------------------------------------------------
34 
35 //----- Namespace declaration --------------------------------------------------
36 
38 namespace xms
39 {
40 //----- Constants / Enumerations -----------------------------------------------
41 
42 //----- Classes / Structs ------------------------------------------------------
43 
44 //----- Internal functions -----------------------------------------------------
45 
46 //----- Class / Function definitions -------------------------------------------
47 
48 namespace
49 {
50 typedef std::tuple<int, int, int> Triangle;
51 typedef boost::container::flat_map<Triangle, double>
52  RatioCache;
53 typedef boost::container::flat_map<Triangle, bool>
54  ValidCache;
55 
56 class XmUGridTriangles2dImpl : public XmUGridTriangles2d
57 {
58 public:
59  XmUGridTriangles2dImpl();
60 
61  virtual void BuildTriangles(const XmUGrid& a_ugrid, bool a_addTriangleCenters) override;
62  virtual void BuildEarcutTriangles(const XmUGrid& a_ugrid) override;
63  virtual void SetCellActivity(const DynBitset& a_cellActivity) override;
64 
65  virtual const VecPt3d& GetPoints() const override;
66  virtual const VecInt& GetTriangles() const override;
67  virtual BSHP<VecPt3d> GetPointsPtr() override;
68  virtual BSHP<VecInt> GetTrianglesPtr() override;
69 
70  int AddCellCentroid(int a_cellIdx, const Pt3d& a_point);
71  void AddCellTriangle(int a_cellIdx, int a_idx1, int a_idx2, int a_idx3);
72 
73  virtual int GetCellCentroid(int a_cellIdx) const override;
74 
75  virtual int GetIntersectedCell(const Pt3d& a_point, VecInt& a_idxs, VecDbl& a_weights) override;
76 
77 private:
78  void Initialize(const XmUGrid& a_ugrid);
79  BSHP<GmTriSearch> GetTriSearch();
80 
81  BSHP<VecPt3d> m_points;
82  BSHP<VecInt> m_triangles;
83  VecInt m_centroidIdxs;
85  mutable BSHP<GmTriSearch> m_triSearch;
86 };
87 
88 //------------------------------------------------------------------------------
91 //------------------------------------------------------------------------------
92 double iMagnitude(const Pt3d& a_vec)
93 {
94  double magnitude = sqrt(a_vec.x * a_vec.x + a_vec.y * a_vec.y + a_vec.z * a_vec.z);
95  return magnitude;
96 } // iMagnitude
97 //------------------------------------------------------------------------------
107 //------------------------------------------------------------------------------
108 double iGetEarcutTriangleRatio(const VecPt3d& a_points,
109  int a_idx1,
110  int a_idx2,
111  int a_idx3,
112  RatioCache& a_ratioCache)
113 {
114  Triangle triangle(a_idx1, a_idx2, a_idx3);
115  auto ratioIter = a_ratioCache.find(triangle);
116  if (ratioIter != a_ratioCache.end())
117  return ratioIter->second;
118 
119  Pt3d pt1 = a_points[a_idx1];
120  Pt3d pt2 = a_points[a_idx2];
121  Pt3d pt3 = a_points[a_idx3];
122  Pt3d v1 = pt1 - pt2;
123  Pt3d v2 = pt3 - pt2;
124  Pt3d v3 = pt3 - pt1;
125 
126  // get 2*area
127  Pt3d cross;
128  gmCross3D(v2, v1, &cross);
129  double ratio;
130  if (cross.z <= 0.0)
131  {
132  // inverted
133  ratio = -1.0;
134  }
135  else
136  {
137  double area2 = iMagnitude(cross);
138  if (area2 == 0.0)
139  {
140  // degenerate triangle
141  ratio = -2.0;
142  }
143  else
144  {
145  double perimeter = iMagnitude(v1) + iMagnitude(v2) + iMagnitude(v3);
146  ratio = perimeter * perimeter / area2;
147  }
148  }
149 
150  a_ratioCache[triangle] = ratio;
151  return ratio;
152 } // iGetEarcutTriangleRatio
153 //------------------------------------------------------------------------------
155 //------------------------------------------------------------------------------
156 bool iValidTriangle(const VecPt3d& a_points,
157  const VecInt a_polygon,
158  int a_idx1,
159  int a_idx2,
160  int a_idx3,
161  ValidCache& a_validCache)
162 {
163  Triangle triangle(a_idx1, a_idx2, a_idx3);
164  auto validIter = a_validCache.find(triangle);
165  if (validIter != a_validCache.end())
166  return validIter->second;
167 
168  const Pt3d& pt1 = a_points[a_idx1];
169  const Pt3d& pt2 = a_points[a_idx2];
170  const Pt3d& pt3 = a_points[a_idx3];
171  for (size_t pointIdx = 0; pointIdx < a_polygon.size(); ++pointIdx)
172  {
173  int idx = a_polygon[pointIdx];
174  if (idx != a_idx1 && idx != a_idx2 && idx != a_idx3)
175  {
176  const Pt3d& pt = a_points[idx];
177  if (gmTurn(pt1, pt2, pt, 0.0) == TURN_LEFT && gmTurn(pt2, pt3, pt) == TURN_LEFT &&
178  gmTurn(pt3, pt1, pt, 0.0) == TURN_LEFT)
179  {
180  a_validCache[triangle] = false;
181  return false;
182  }
183  }
184  }
185 
186  a_validCache[triangle] = true;
187  return true;
188 } // iValidTriangle
189 //------------------------------------------------------------------------------
196 //------------------------------------------------------------------------------
197 bool iGenerateCentroidTriangles(XmUGridTriangles2dImpl& a_ugridTris,
198  int a_cellIdx,
199  const VecInt& a_polygonIdxs)
200 {
201  const VecPt3d& points = a_ugridTris.GetPoints();
202  size_t numPoints = a_polygonIdxs.size();
203 
204  VecPt3d polygon;
205  for (size_t pointIdx = 0; pointIdx < numPoints; ++pointIdx)
206  polygon.push_back(points[a_polygonIdxs[pointIdx]]);
207 
208  Pt3d centroid = gmComputeCentroid(polygon);
209 
210  // make sure none of the triangles connected to the centroid are inverted
211  for (size_t pointIdx = 0; pointIdx < polygon.size(); ++pointIdx)
212  {
213  const Pt3d& pt1 = polygon[pointIdx];
214  const Pt3d& pt2 = polygon[(pointIdx + 1) % numPoints];
215 
216  // centroid should be to the left of each edge
217  if (gmTurn(pt1, pt2, centroid, 0.0) != TURN_LEFT)
218  return false;
219  }
220 
221  // add centroid to list of points
222  int centroidIdx = a_ugridTris.AddCellCentroid(a_cellIdx, centroid);
223 
224  // add triangles
225  for (size_t pointIdx = 0; pointIdx < polygon.size(); ++pointIdx)
226  {
227  int idx1 = a_polygonIdxs[pointIdx];
228  int idx2 = a_polygonIdxs[(pointIdx + 1) % numPoints];
229  a_ugridTris.AddCellTriangle(a_cellIdx, idx1, idx2, centroidIdx);
230  }
231 
232  return true;
233 } // iGenerateCentroidTriangles
234 //------------------------------------------------------------------------------
239 //------------------------------------------------------------------------------
240 void iBuildEarcutTriangles(XmUGridTriangles2dImpl& a_ugridTris,
241  int a_cellIdx,
242  const VecInt& a_polygonIdxs)
243 {
244  VecInt polygonIdxs = a_polygonIdxs;
245  const VecPt3d& points = a_ugridTris.GetPoints();
246 
247  // continually find best triangle on adjacent edges and cut it off polygon
248  RatioCache ratioCache;
249  ValidCache validCache;
250  while (polygonIdxs.size() >= 4)
251  {
252  int bestIdx = -1;
253  int secondBestIdx = -1;
254 
255  int numPoints = (int)polygonIdxs.size();
256  double bestRatio = std::numeric_limits<double>::max();
257  double secondBestRatio = std::numeric_limits<double>::max();
258  for (int pointIdx = 0; pointIdx < numPoints; ++pointIdx)
259  {
260  int idx1 = polygonIdxs[(pointIdx + numPoints - 1) % numPoints];
261  int idx2 = polygonIdxs[pointIdx];
262  int idx3 = polygonIdxs[(pointIdx + 1) % numPoints];
263 
264  // make sure triangle is valid (not inverted and doesn't have other points in it)
265  double ratio = iGetEarcutTriangleRatio(points, idx1, idx2, idx3, ratioCache);
266  if (ratio > 0.0)
267  {
268  if (iValidTriangle(points, polygonIdxs, idx1, idx2, idx3, validCache))
269  {
270  if (ratio < bestRatio)
271  {
272  secondBestRatio = bestRatio;
273  bestRatio = ratio;
274  secondBestIdx = bestIdx;
275  bestIdx = pointIdx;
276  }
277  else if (ratio < secondBestRatio)
278  {
279  secondBestRatio = ratio;
280  secondBestIdx = pointIdx;
281  }
282  }
283  }
284  }
285 
286  if (bestIdx >= 0)
287  {
288  if (numPoints == 4 && secondBestIdx >= 0)
289  bestIdx = secondBestIdx;
290 
291  // cut off the ear triangle
292  int polygonIdx1 = (bestIdx + numPoints - 1) % numPoints;
293  int polygonIdx2 = bestIdx;
294  int polygonIdx3 = (bestIdx + 1) % numPoints;
295  int idx1 = polygonIdxs[polygonIdx1];
296  int idx2 = polygonIdxs[polygonIdx2];
297  int idx3 = polygonIdxs[polygonIdx3];
298  a_ugridTris.AddCellTriangle(a_cellIdx, idx1, idx2, idx3);
299  polygonIdxs.erase(polygonIdxs.begin() + polygonIdx2);
300  }
301  else
302  {
303  std::ostringstream ss;
304  ss << "Unable to split cell number " << a_cellIdx + 1 << " into triangles.";
305  XM_LOG(xmlog::error, ss.str());
306  return;
307  }
308  }
309 
310  // push on remaining triangle
311  a_ugridTris.AddCellTriangle(a_cellIdx, polygonIdxs[0], polygonIdxs[1], polygonIdxs[2]);
312 } // iBuildEarcutTriangles
313 
319 //------------------------------------------------------------------------------
321 //------------------------------------------------------------------------------
322 XmUGridTriangles2dImpl::XmUGridTriangles2dImpl()
323 : m_points(new VecPt3d)
324 , m_triangles(new VecInt)
325 , m_centroidIdxs()
327 , m_triSearch()
328 {
329 } // XmUGridTriangles2dImpl::XmUGridTriangles2dImpl
330 //------------------------------------------------------------------------------
334 //------------------------------------------------------------------------------
335 void XmUGridTriangles2dImpl::BuildTriangles(const XmUGrid& a_ugrid, bool a_addTriangleCenters)
336 {
337  Initialize(a_ugrid);
338 
339  int numCells = a_ugrid.GetCellCount();
340  VecInt cellPoints;
341  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx)
342  {
343  a_ugrid.GetCellPoints(cellIdx, cellPoints);
344  bool builtTriangles = false;
345  if (a_addTriangleCenters)
346  builtTriangles = iGenerateCentroidTriangles(*this, cellIdx, cellPoints);
347  if (!builtTriangles)
348  iBuildEarcutTriangles(*this, cellIdx, cellPoints);
349  }
350 } // XmUGridTriangles2dImpl::BuildTriangles
351 //------------------------------------------------------------------------------
354 //------------------------------------------------------------------------------
355 void XmUGridTriangles2dImpl::BuildEarcutTriangles(const XmUGrid& a_ugrid)
356 {
357  Initialize(a_ugrid);
358 
359  int numCells = a_ugrid.GetCellCount();
360  VecInt cellPoints;
361  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx)
362  {
363  a_ugrid.GetCellPoints(cellIdx, cellPoints);
364  iBuildEarcutTriangles(*this, cellIdx, cellPoints);
365  }
366 } // XmUGridTriangles2dImpl::BuildEarcutTriangles
367 //------------------------------------------------------------------------------
369 //------------------------------------------------------------------------------
370 void XmUGridTriangles2dImpl::SetCellActivity(const DynBitset& a_cellActivity)
371 {
372  if (a_cellActivity.empty())
373  {
374  DynBitset emptyActivity;
375  GetTriSearch()->SetTriActivity(emptyActivity);
376  return;
377  }
378 
379  DynBitset triangleActivity;
380  int numTriangles = (int)m_triangleToCellIdx.size();
381  triangleActivity.resize(numTriangles);
382  for (size_t triangleIdx = 0; triangleIdx < numTriangles; ++triangleIdx)
383  {
384  int cellIdx = m_triangleToCellIdx[triangleIdx];
385  triangleActivity[triangleIdx] = cellIdx >= a_cellActivity.size() || a_cellActivity[cellIdx];
386  }
387  GetTriSearch()->SetTriActivity(triangleActivity);
388 } // XmUGridTriangles2dImpl::SetCellActivity
389 //------------------------------------------------------------------------------
392 //------------------------------------------------------------------------------
393 const VecPt3d& XmUGridTriangles2dImpl::GetPoints() const
394 {
395  return *m_points;
396 } // XmUGridTriangles2dImpl::GetPoints
397 //------------------------------------------------------------------------------
400 //------------------------------------------------------------------------------
401 const VecInt& XmUGridTriangles2dImpl::GetTriangles() const
402 {
403  return *m_triangles;
404 } // XmUGridTriangles2dImpl::GetTriangles
405 //------------------------------------------------------------------------------
408 //------------------------------------------------------------------------------
409 BSHP<VecPt3d> XmUGridTriangles2dImpl::GetPointsPtr()
410 {
411  return m_points;
412 } // XmUGridTriangles2dImpl::GetPointsPtr
413 //------------------------------------------------------------------------------
416 //------------------------------------------------------------------------------
417 BSHP<VecInt> XmUGridTriangles2dImpl::GetTrianglesPtr()
418 {
419  return m_triangles;
420 } // XmUGridTriangles2dImpl::GetTrianglesPtr
421 //------------------------------------------------------------------------------
426 //------------------------------------------------------------------------------
427 int XmUGridTriangles2dImpl::AddCellCentroid(int a_cellIdx, const Pt3d& a_point)
428 {
429  int centroidIdx = (int)m_points->size();
430  m_points->push_back(a_point);
431  m_centroidIdxs[a_cellIdx] = centroidIdx;
432  return centroidIdx;
433 } // XmUGridTriangles2dImpl::AddCellCentroid
434 //------------------------------------------------------------------------------
440 //------------------------------------------------------------------------------
441 void XmUGridTriangles2dImpl::AddCellTriangle(int a_cellIdx, int a_idx1, int a_idx2, int a_idx3)
442 {
443  m_triangles->push_back(a_idx1);
444  m_triangles->push_back(a_idx2);
445  m_triangles->push_back(a_idx3);
446  m_triangleToCellIdx.push_back(a_cellIdx);
447 } // XmUGridTriangles2dImpl::AddCellTriangle
448 //------------------------------------------------------------------------------
452 //------------------------------------------------------------------------------
453 int XmUGridTriangles2dImpl::GetCellCentroid(int a_cellIdx) const
454 {
455  int pointIdx = -1;
456  if (a_cellIdx >= 0 && a_cellIdx < (int)m_centroidIdxs.size())
457  pointIdx = m_centroidIdxs[a_cellIdx];
458  return pointIdx;
459 } // XmUGridTriangles2dImpl::GetCellCentroid
460 //------------------------------------------------------------------------------
466 //------------------------------------------------------------------------------
467 int XmUGridTriangles2dImpl::GetIntersectedCell(const Pt3d& a_point,
468  VecInt& a_idxs,
469  VecDbl& a_weights)
470 {
471  if (!m_triSearch)
472  GetTriSearch();
473  int cellIdx = -1;
474  int triangleLocation;
475  if (m_triSearch->InterpWeightsTriangleIdx(a_point, triangleLocation, a_idxs, a_weights))
476  {
477  int triangleIdx = triangleLocation / 3;
478  cellIdx = m_triangleToCellIdx[triangleIdx];
479  }
480  return cellIdx;
481 } // XmUGridTriangles2dImpl::GetIntersectedCell
482 //------------------------------------------------------------------------------
485 //------------------------------------------------------------------------------
486 void XmUGridTriangles2dImpl::Initialize(const XmUGrid& a_ugrid)
487 {
488  *m_points = a_ugrid.GetLocations();
489  m_triangles->clear();
490  m_centroidIdxs.assign(a_ugrid.GetCellCount(), -1);
491  m_triangleToCellIdx.clear();
492  m_triSearch.reset();
493 } // XmUGridTriangles2dImpl::Initialize
494 //------------------------------------------------------------------------------
496 //------------------------------------------------------------------------------
497 BSHP<GmTriSearch> XmUGridTriangles2dImpl::GetTriSearch()
498 {
499  if (!m_triSearch)
500  {
501  m_triSearch = GmTriSearch::New();
502  m_triSearch->TrisToSearch(m_points, m_triangles);
503  }
504  return m_triSearch;
505 } // XmUGridTriangles2dImpl::GetTriSearch
506 
507 } // namespace
508 
514 //------------------------------------------------------------------------------
517 //------------------------------------------------------------------------------
518 BSHP<XmUGridTriangles2d> XmUGridTriangles2d::New()
519 {
520  BSHP<XmUGridTriangles2d> triangles(new XmUGridTriangles2dImpl);
521  return triangles;
522 } // XmUGridTriangles2d::New
523 //------------------------------------------------------------------------------
525 //------------------------------------------------------------------------------
527 {
528 } // XmUGridTriangles2d::XmUGridTriangles2d
529 //------------------------------------------------------------------------------
531 //------------------------------------------------------------------------------
533 {
534 } // XmUGridTriangles2d::XmUGridTriangles2d
535 
536 } // namespace xms
537 
538 #ifdef CXX_TEST
539 //------------------------------------------------------------------------------
540 // Unit Tests
541 //------------------------------------------------------------------------------
542 using namespace xms;
544 
545 #include <xmscore/testing/TestTools.h>
546 
551 //------------------------------------------------------------------------------
553 //------------------------------------------------------------------------------
555 {
556  VecPt3d points = {{0, 0, 0}, {1, 0, 0}, {0.5, 1, 0}};
557  VecInt cells = {XMU_TRIANGLE, 3, 0, 1, 2};
558  std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
559  TS_REQUIRE_NOT_NULL(ugrid);
560  XmUGridTriangles2dImpl triangles;
561 
562  // test building cetroid in triangle cell
563  triangles.BuildTriangles(*ugrid, true);
564 
565  VecPt3d triPointsOut = triangles.GetPoints();
566  VecPt3d triPointsExpected = {{0, 0, 0}, {1, 0, 0}, {0.5, 1, 0}, {0.5, 1 / 3.0, 0}};
567  TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
568 
569  VecInt trianglesOut = triangles.GetTriangles();
570  VecInt trianglesExpected = {0, 1, 3, 1, 2, 3, 2, 0, 3};
571  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
572 
573  TS_ASSERT_EQUALS(3, triangles.GetCellCentroid(0));
574 
575  VecInt idxs;
576  VecDbl weights;
577  // point intersecting last triangle
578  int cellIdx = triangles.GetIntersectedCell(Pt3d(0.25, 0.25, 0), idxs, weights);
579  TS_ASSERT_EQUALS(0, cellIdx);
580  VecInt idxsExpected = {2, 0, 3};
581  TS_ASSERT_EQUALS(idxsExpected, idxs);
582  VecDbl weightsExpected = {0.125, 0.5, 0.375};
583  TS_ASSERT_EQUALS(weightsExpected, weights);
584  cellIdx = triangles.GetIntersectedCell(Pt3d(0.75, 0.25, 0), idxs, weights);
585  TS_ASSERT_EQUALS(0, cellIdx);
586  idxsExpected = {1, 2, 3};
587  TS_ASSERT_EQUALS(idxsExpected, idxs);
588  weightsExpected = {0.5, 0.125, 0.375};
589  TS_ASSERT_EQUALS(weightsExpected, weights);
590  cellIdx = triangles.GetIntersectedCell(Pt3d(0.5, 0.25, 0), idxs, weights);
591  TS_ASSERT_EQUALS(0, cellIdx);
592  idxsExpected = {0, 1, 3};
593  TS_ASSERT_EQUALS(idxsExpected, idxs);
594  weightsExpected = {0.125, 0.125, 0.75};
595  TS_ASSERT_EQUALS(weightsExpected, weights);
596 
597  // test without building centroid in triangle cell
598  triangles.BuildTriangles(*ugrid, false);
599 
600  triPointsOut = triangles.GetPoints();
601  TS_ASSERT_EQUALS(points, triPointsOut);
602 
603  trianglesOut = triangles.GetTriangles();
604  trianglesExpected = {0, 1, 2};
605  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
606 
607  TS_ASSERT_EQUALS(-1, triangles.GetCellCentroid(0));
608 
609  cellIdx = triangles.GetIntersectedCell(Pt3d(0.5, 0.25, 0), idxs, weights);
610  TS_ASSERT_EQUALS(0, cellIdx);
611  idxsExpected = {0, 1, 2};
612  TS_ASSERT_EQUALS(idxsExpected, idxs);
613  weightsExpected = {0.375, 0.375, 0.25};
614  TS_ASSERT_EQUALS(weightsExpected, weights);
615 
616 } // XmUGridTriangles2dUnitTests::testBuildCentroidTrianglesOnTriangle
617 //------------------------------------------------------------------------------
619 //------------------------------------------------------------------------------
621 {
622  VecPt3d points = {{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}};
623  VecInt cells = {XMU_QUAD, 4, 0, 1, 2, 3};
624  std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
625  TS_REQUIRE_NOT_NULL(ugrid);
626  XmUGridTriangles2dImpl triangles;
627  triangles.BuildTriangles(*ugrid, true);
628  VecPt3d triPointsOut = triangles.GetPoints();
629  VecPt3d triPointsExpected = {{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0.5, 0.5, 0}};
630  TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
631 
632  VecInt trianglesOut = triangles.GetTriangles();
633  VecInt trianglesExpected = {0, 1, 4, 1, 2, 4, 2, 3, 4, 3, 0, 4};
634  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
635 
636  TS_ASSERT_EQUALS(4, triangles.GetCellCentroid(0));
637 } // XmUGridTriangles2dUnitTests::testBuildCentroidTrianglesOnQuad
638 //------------------------------------------------------------------------------
640 //------------------------------------------------------------------------------
642 {
643  // clang-format off
644  VecPt3d points = {
645  {0, 0, 0}, // 0
646  {10, 0, 0}, // 1
647  {20, 0, 0}, // 2
648  {30, 0, 0}, // 3
649  {40, 0, 0}, // 4
650  {0, 10, 0}, // 5
651  {10, 10, 0}, // 6
652  {20, 10, 0}, // 7
653  {40, 10, 0}, // 8
654  {0, 20, 0}, // 9
655  {10, 20, 0}, // 10
656  {20, 20, 0}, // 11
657  {30, 20, 0}, // 12
658  {40, 20, 0} // 13
659  };
660 
661  // Cell type (5), number of points (3), point numbers, counterclockwise
662  std::vector<int> cells = {(int)XMU_QUAD, 4, 0, 1, 6, 5, // 0
663  (int)XMU_PIXEL, 4, 1, 2, 6, 7, // 1
664  (int)XMU_TRIANGLE, 3, 2, 3, 7, // 2
665  (int)XMU_POLYGON, 6, 3, 4, 8, 13, 12, 7}; // 3
666  // clang-format on
667 
668  std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
669  TS_REQUIRE_NOT_NULL(ugrid);
670  XmUGridTriangles2dImpl triangles;
671  triangles.BuildTriangles(*ugrid, true);
672 
673  VecPt3d triPointsOut = triangles.GetPoints();
674  // clang-format off
675  VecPt3d triPointsExpected = {
676  {0.0, 0.0, 0.0},
677  {10.0, 0.0, 0.0},
678  {20.0, 0.0, 0.0},
679  {30.0, 0.0, 0.0},
680  {40.0, 0.0, 0.0},
681  {0.0, 10.0, 0.0},
682  {10.0, 10.0, 0.0},
683  {20.0, 10.0, 0.0},
684  {40.0, 10.0, 0.0},
685  {0.0, 20.0, 0.0},
686  {10.0, 20.0, 0.0},
687  {20.0, 20.0, 0.0},
688  {30.0, 20.0, 0.0},
689  {40.0, 20.0, 0.0},
690 
691  // added centroids
692  {5.0, 5.0, 0.0}, // 14 - quad centroid
693  {15.0, 5.0, 0.0}, // 15 - pixel centroid
694  {23.333333333333332, 3.3333333333333335, 0.0}, // 16 - triangle centroid
695  {33.333333333333336, 10.0, 0.0} // 17 - polygon centroid
696  };
697  // clang-format on
698  TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
699 
700  VecInt trianglesOut = triangles.GetTriangles();
701  // clang-format off
702  VecInt trianglesExpected = {
703  0, 1, 14, 1, 6, 14, 6, 5, 14, 5, 0, 14, // quad
704  1, 2, 15, 2, 7, 15, 7, 6, 15, 6, 1, 15, // pixel
705  2, 3, 16, 3, 7, 16, 7, 2, 16, // triangle
706  3, 4, 17, 4, 8, 17, 8, 13, 17, 13, 12, 17, 12, 7, 17, 7, 3, 17}; // polygon
707  // clang-format on
708  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
709 } // XmUGridTriangles2dUnitTests::testBuildCentroidTriangles2dCellTypes
710 //------------------------------------------------------------------------------
712 //------------------------------------------------------------------------------
714 {
715  {
716  // V shaped cell
717  // clang-format off
718  VecPt3d points = {
719  {0, 0, 0}, // 0
720  {10, 0, 0}, // 1
721  {1, 1, 0}, // 2
722  {0, 10, 0}, // 3
723  {10, 10, 0} // 4
724  };
725 
726  std::vector<int> cells = {
727  XMU_QUAD, 4, 0, 1, 2, 3,
728  };
729  // clang-format on
730 
731  std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
732  XmUGridTriangles2dImpl ugridTris;
733 
734  ugridTris.BuildTriangles(*ugrid, true);
735 
736  VecPt3d triPointsOut = ugridTris.GetPoints();
737  VecPt3d triPointsExpected = points;
738  TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
739 
740  VecInt trianglesOut = ugridTris.GetTriangles();
741  VecInt trianglesExpected = {2, 3, 0, 0, 1, 2};
742  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
743  }
744 
745  {
746  // house shaped cell
747  // clang-format off
748  VecPt3d points = {
749  {0, 0, 0}, // 0
750  {10, 0, 0}, // 1
751  {10, 10, 0}, // 2
752  {5, 11, 0}, // 3
753  {0, 10, 0}, // 4
754  };
755 
756  std::vector<int> cells = {(int)XMU_POLYGON, 5, 0, 1, 2, 3, 4};
757  // clang-format on
758 
759  std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
760  XmUGridTriangles2dImpl ugridTris;
761  ugridTris.BuildEarcutTriangles(*ugrid);
762 
763  VecPt3d triPointsOut = ugridTris.GetPoints();
764  VecPt3d triPointsExpected = points;
765  TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
766 
767  VecInt trianglesOut = ugridTris.GetTriangles();
768  VecInt trianglesExpected = {4, 0, 1, 1, 2, 3, 1, 3, 4};
769  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
770  }
771 } // XmUGridTriangles2dUnitTests::testBuildEarcutTriangles
772 //------------------------------------------------------------------------------
774 //------------------------------------------------------------------------------
776 {
777  // 7----6---------3----2
778  // | | | |
779  // | | | |
780  // | | | |
781  // | | (8) | |
782  // | | | |
783  // | | | |
784  // | | | |
785  // | 5---------4 |
786  // | |
787  // | |
788  // | |
789  // 0-------------------1
790  // clang-format off
791  VecPt3d points = {
792  { 0, 0, 0}, // 0
793  {15, 0, 0}, // 1
794  {15, 15, 0}, // 2
795  {10, 15, 0}, // 3
796  {10, 5, 0}, // 4
797  { 5, 5, 0}, // 5
798  { 5, 15, 0}, // 6
799  { 0, 15, 0} // 7
800  };
801 
802  // Cell type (5), number of points (3), point numbers, counterclockwise
803  std::vector<int> cells = {
804  XMU_POLYGON, 8, 0, 1, 2, 3, 4, 5, 6, 7,
805  XMU_QUAD, 4, 3, 6, 5, 4
806  };
807  // clang-format on
808 
809  std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
810  XmUGridTriangles2dImpl ugridTris;
811 
812  ugridTris.BuildTriangles(*ugrid, true);
813 
814  VecPt3d triPointsOut = ugridTris.GetPoints();
815  VecPt3d triPointsExpected = points;
816  triPointsExpected.push_back({7.5, 10, 0});
817  TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
818 
819  VecInt trianglesOut = ugridTris.GetTriangles();
820  VecInt trianglesExpected = {
821  // clang-format off
822  2, 3, 4, 5, 6, 7, 1, 2, 4, 0, 1, 4, 0, 4, 5, 0, 5, 7, // earcut
823  3, 6, 8, 6, 5, 8, 5, 4, 8, 4, 3, 8 // centroid
824  // clang-format off
825  };
826  TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
827 } // XmUGridTriangles2dUnitTests::testBuildCentroidAndEarcutTriangles
828 
829 #endif
VecInt m_triangleToCellIdx
The cell index for each triangle.
void testBuildCentroidTriangles2dCellTypes()
Test creating triangles using cell centroid for linear 2D cell types.
XmUGridTriangles2d()
Default contructor.
void testBuildEarcutTriangles()
Test creating plan view 2D triangles using ear cut algorithm.
Class to store XmUGrid triangles. Tracks where midpoints and triangles came from. ...
void testBuildCentroidTrianglesOnTriangle()
Test creating triangles using cell centroid.
BSHP< VecPt3d > m_points
Triangle points for the UGrid.
void testBuildCentroidAndEarcutTriangles()
Test creating triangles for centroid and earcut cells.
virtual ~XmUGridTriangles2d()
Destructor.
VecInt m_centroidIdxs
Index of each cell centroid or -1 if none.
BSHP< GmTriSearch > m_triSearch
Triangle searcher for triangles.
BSHP< VecInt > m_triangles
Triangles for the UGrid.
XMS Namespace.
Contains the XmUGrid Class and supporting data types.
void testBuildCentroidTrianglesOnQuad()
Test creating triangles on a quad using cell centroid.