20 #include <boost/container/flat_map.hpp> 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> 50 typedef std::tuple<int, int, int> Triangle;
51 typedef boost::container::flat_map<Triangle, double>
53 typedef boost::container::flat_map<Triangle, bool>
59 XmUGridTriangles2dImpl();
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;
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;
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);
73 virtual int GetCellCentroid(
int a_cellIdx)
const override;
75 virtual int GetIntersectedCell(
const Pt3d& a_point, VecInt& a_idxs, VecDbl& a_weights)
override;
78 void Initialize(
const XmUGrid& a_ugrid);
79 BSHP<GmTriSearch> GetTriSearch();
92 double iMagnitude(
const Pt3d& a_vec)
94 double magnitude = sqrt(a_vec.x * a_vec.x + a_vec.y * a_vec.y + a_vec.z * a_vec.z);
108 double iGetEarcutTriangleRatio(
const VecPt3d& a_points,
112 RatioCache& a_ratioCache)
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;
119 Pt3d pt1 = a_points[a_idx1];
120 Pt3d pt2 = a_points[a_idx2];
121 Pt3d pt3 = a_points[a_idx3];
128 gmCross3D(v2, v1, &cross);
137 double area2 = iMagnitude(cross);
145 double perimeter = iMagnitude(v1) + iMagnitude(v2) + iMagnitude(v3);
146 ratio = perimeter * perimeter / area2;
150 a_ratioCache[triangle] = ratio;
156 bool iValidTriangle(
const VecPt3d& a_points,
157 const VecInt a_polygon,
161 ValidCache& a_validCache)
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;
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)
173 int idx = a_polygon[pointIdx];
174 if (idx != a_idx1 && idx != a_idx2 && idx != a_idx3)
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)
180 a_validCache[triangle] =
false;
186 a_validCache[triangle] =
true;
197 bool iGenerateCentroidTriangles(XmUGridTriangles2dImpl& a_ugridTris,
199 const VecInt& a_polygonIdxs)
201 const VecPt3d& points = a_ugridTris.GetPoints();
202 size_t numPoints = a_polygonIdxs.size();
205 for (
size_t pointIdx = 0; pointIdx < numPoints; ++pointIdx)
206 polygon.push_back(points[a_polygonIdxs[pointIdx]]);
208 Pt3d centroid = gmComputeCentroid(polygon);
211 for (
size_t pointIdx = 0; pointIdx < polygon.size(); ++pointIdx)
213 const Pt3d& pt1 = polygon[pointIdx];
214 const Pt3d& pt2 = polygon[(pointIdx + 1) % numPoints];
217 if (gmTurn(pt1, pt2, centroid, 0.0) != TURN_LEFT)
222 int centroidIdx = a_ugridTris.AddCellCentroid(a_cellIdx, centroid);
225 for (
size_t pointIdx = 0; pointIdx < polygon.size(); ++pointIdx)
227 int idx1 = a_polygonIdxs[pointIdx];
228 int idx2 = a_polygonIdxs[(pointIdx + 1) % numPoints];
229 a_ugridTris.AddCellTriangle(a_cellIdx, idx1, idx2, centroidIdx);
240 void iBuildEarcutTriangles(XmUGridTriangles2dImpl& a_ugridTris,
242 const VecInt& a_polygonIdxs)
244 VecInt polygonIdxs = a_polygonIdxs;
245 const VecPt3d& points = a_ugridTris.GetPoints();
248 RatioCache ratioCache;
249 ValidCache validCache;
250 while (polygonIdxs.size() >= 4)
253 int secondBestIdx = -1;
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)
260 int idx1 = polygonIdxs[(pointIdx + numPoints - 1) % numPoints];
261 int idx2 = polygonIdxs[pointIdx];
262 int idx3 = polygonIdxs[(pointIdx + 1) % numPoints];
265 double ratio = iGetEarcutTriangleRatio(points, idx1, idx2, idx3, ratioCache);
268 if (iValidTriangle(points, polygonIdxs, idx1, idx2, idx3, validCache))
270 if (ratio < bestRatio)
272 secondBestRatio = bestRatio;
274 secondBestIdx = bestIdx;
277 else if (ratio < secondBestRatio)
279 secondBestRatio = ratio;
280 secondBestIdx = pointIdx;
288 if (numPoints == 4 && secondBestIdx >= 0)
289 bestIdx = secondBestIdx;
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);
303 std::ostringstream ss;
304 ss <<
"Unable to split cell number " << a_cellIdx + 1 <<
" into triangles.";
305 XM_LOG(xmlog::error, ss.str());
311 a_ugridTris.AddCellTriangle(a_cellIdx, polygonIdxs[0], polygonIdxs[1], polygonIdxs[2]);
322 XmUGridTriangles2dImpl::XmUGridTriangles2dImpl()
335 void XmUGridTriangles2dImpl::BuildTriangles(
const XmUGrid& a_ugrid,
bool a_addTriangleCenters)
339 int numCells = a_ugrid.GetCellCount();
341 for (
int cellIdx = 0; cellIdx < numCells; ++cellIdx)
343 a_ugrid.GetCellPoints(cellIdx, cellPoints);
344 bool builtTriangles =
false;
345 if (a_addTriangleCenters)
346 builtTriangles = iGenerateCentroidTriangles(*
this, cellIdx, cellPoints);
348 iBuildEarcutTriangles(*
this, cellIdx, cellPoints);
355 void XmUGridTriangles2dImpl::BuildEarcutTriangles(
const XmUGrid& a_ugrid)
359 int numCells = a_ugrid.GetCellCount();
361 for (
int cellIdx = 0; cellIdx < numCells; ++cellIdx)
363 a_ugrid.GetCellPoints(cellIdx, cellPoints);
364 iBuildEarcutTriangles(*
this, cellIdx, cellPoints);
370 void XmUGridTriangles2dImpl::SetCellActivity(
const DynBitset& a_cellActivity)
372 if (a_cellActivity.empty())
374 DynBitset emptyActivity;
375 GetTriSearch()->SetTriActivity(emptyActivity);
379 DynBitset triangleActivity;
381 triangleActivity.resize(numTriangles);
382 for (
size_t triangleIdx = 0; triangleIdx < numTriangles; ++triangleIdx)
385 triangleActivity[triangleIdx] = cellIdx >= a_cellActivity.size() || a_cellActivity[cellIdx];
387 GetTriSearch()->SetTriActivity(triangleActivity);
393 const VecPt3d& XmUGridTriangles2dImpl::GetPoints()
const 401 const VecInt& XmUGridTriangles2dImpl::GetTriangles()
const 409 BSHP<VecPt3d> XmUGridTriangles2dImpl::GetPointsPtr()
417 BSHP<VecInt> XmUGridTriangles2dImpl::GetTrianglesPtr()
427 int XmUGridTriangles2dImpl::AddCellCentroid(
int a_cellIdx,
const Pt3d& a_point)
429 int centroidIdx = (int)
m_points->size();
441 void XmUGridTriangles2dImpl::AddCellTriangle(
int a_cellIdx,
int a_idx1,
int a_idx2,
int a_idx3)
453 int XmUGridTriangles2dImpl::GetCellCentroid(
int a_cellIdx)
const 467 int XmUGridTriangles2dImpl::GetIntersectedCell(
const Pt3d& a_point,
474 int triangleLocation;
475 if (
m_triSearch->InterpWeightsTriangleIdx(a_point, triangleLocation, a_idxs, a_weights))
477 int triangleIdx = triangleLocation / 3;
486 void XmUGridTriangles2dImpl::Initialize(
const XmUGrid& a_ugrid)
497 BSHP<GmTriSearch> XmUGridTriangles2dImpl::GetTriSearch()
518 BSHP<XmUGridTriangles2d> XmUGridTriangles2d::New()
520 BSHP<XmUGridTriangles2d> triangles(
new XmUGridTriangles2dImpl);
545 #include <xmscore/testing/TestTools.h> 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;
563 triangles.BuildTriangles(*ugrid,
true);
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);
569 VecInt trianglesOut = triangles.GetTriangles();
570 VecInt trianglesExpected = {0, 1, 3, 1, 2, 3, 2, 0, 3};
571 TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
573 TS_ASSERT_EQUALS(3, triangles.GetCellCentroid(0));
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);
598 triangles.BuildTriangles(*ugrid,
false);
600 triPointsOut = triangles.GetPoints();
601 TS_ASSERT_EQUALS(points, triPointsOut);
603 trianglesOut = triangles.GetTriangles();
604 trianglesExpected = {0, 1, 2};
605 TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
607 TS_ASSERT_EQUALS(-1, triangles.GetCellCentroid(0));
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);
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);
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);
636 TS_ASSERT_EQUALS(4, triangles.GetCellCentroid(0));
662 std::vector<int> cells = {(int)XMU_QUAD, 4, 0, 1, 6, 5,
663 (
int)XMU_PIXEL, 4, 1, 2, 6, 7,
664 (int)XMU_TRIANGLE, 3, 2, 3, 7,
665 (
int)XMU_POLYGON, 6, 3, 4, 8, 13, 12, 7};
668 std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
669 TS_REQUIRE_NOT_NULL(ugrid);
670 XmUGridTriangles2dImpl triangles;
671 triangles.BuildTriangles(*ugrid,
true);
673 VecPt3d triPointsOut = triangles.GetPoints();
675 VecPt3d triPointsExpected = {
694 {23.333333333333332, 3.3333333333333335, 0.0},
695 {33.333333333333336, 10.0, 0.0}
698 TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
700 VecInt trianglesOut = triangles.GetTriangles();
702 VecInt trianglesExpected = {
703 0, 1, 14, 1, 6, 14, 6, 5, 14, 5, 0, 14,
704 1, 2, 15, 2, 7, 15, 7, 6, 15, 6, 1, 15,
705 2, 3, 16, 3, 7, 16, 7, 2, 16,
706 3, 4, 17, 4, 8, 17, 8, 13, 17, 13, 12, 17, 12, 7, 17, 7, 3, 17};
708 TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
726 std::vector<int> cells = {
727 XMU_QUAD, 4, 0, 1, 2, 3,
731 std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
732 XmUGridTriangles2dImpl ugridTris;
734 ugridTris.BuildTriangles(*ugrid,
true);
736 VecPt3d triPointsOut = ugridTris.GetPoints();
737 VecPt3d triPointsExpected = points;
738 TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
740 VecInt trianglesOut = ugridTris.GetTriangles();
741 VecInt trianglesExpected = {2, 3, 0, 0, 1, 2};
742 TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
756 std::vector<int> cells = {(int)XMU_POLYGON, 5, 0, 1, 2, 3, 4};
759 std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
760 XmUGridTriangles2dImpl ugridTris;
761 ugridTris.BuildEarcutTriangles(*ugrid);
763 VecPt3d triPointsOut = ugridTris.GetPoints();
764 VecPt3d triPointsExpected = points;
765 TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
767 VecInt trianglesOut = ugridTris.GetTriangles();
768 VecInt trianglesExpected = {4, 0, 1, 1, 2, 3, 1, 3, 4};
769 TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
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
809 std::shared_ptr<XmUGrid> ugrid = XmUGrid::New(points, cells);
810 XmUGridTriangles2dImpl ugridTris;
812 ugridTris.BuildTriangles(*ugrid,
true);
814 VecPt3d triPointsOut = ugridTris.GetPoints();
815 VecPt3d triPointsExpected = points;
816 triPointsExpected.push_back({7.5, 10, 0});
817 TS_ASSERT_EQUALS(triPointsExpected, triPointsOut);
819 VecInt trianglesOut = ugridTris.GetTriangles();
820 VecInt trianglesExpected = {
822 2, 3, 4, 5, 6, 7, 1, 2, 4, 0, 1, 4, 0, 4, 5, 0, 5, 7,
823 3, 6, 8, 6, 5, 8, 5, 4, 8, 4, 3, 8
826 TS_ASSERT_EQUALS(trianglesExpected, trianglesOut);
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.
Contains the XmUGrid Class and supporting data types.
void testBuildCentroidTrianglesOnQuad()
Test creating triangles on a quad using cell centroid.