xmsmesh  1.0
MeQuadBlossom.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 <cmath>
17 #include <numeric>
18 
19 // 4. External library headers
20 
21 // 5. Shared code headers
22 #include <xmscore/misc/XmError.h>
23 #include <xmscore/misc/xmstype.h>
24 #include <xmsgrid/ugrid/XmEdge.h>
25 #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 
37 namespace xms
38 {
39 //----- Constants / Enumerations -----------------------------------------------
40 const double PI_OVER_TWO = XM_PI / 2.0;
41 const double TWO_OVER_PI = 2.0 / XM_PI;
42 const double TWO_PI = 2.0 * XM_PI;
43 const int DEFAULT_COST = -1000;
44 //----- Classes / Structs ------------------------------------------------------
45 
46 namespace
47 {
50 struct Edge
51 {
53  bool operator==(const Edge& a_rhs) const
54  {
55  return m_p0 == a_rhs.m_p0 && m_p1 == a_rhs.m_p1 && m_fL == a_rhs.m_fL && m_fR == a_rhs.m_fR &&
56  m_pL == a_rhs.m_pL && m_pR == a_rhs.m_pR;
57  }
58 
59  int m_p0;
60  int m_p1;
61  int m_fL;
62  int m_fR;
63  int m_pL;
64  int m_pR;
65 };
66 
68 typedef std::vector<Edge> VecEdge;
69 
71 struct CellData
72 {
74  CellData(int a_priorPt, int a_nextPt, int a_cell)
75  : m_priorPoint(a_priorPt)
76  , m_nextPoint(a_nextPt)
77  , m_cell(a_cell)
78  {
79  }
80 
81  // Equals operator.
82  bool operator==(const CellData& a_rhs) const
83  {
84  return m_priorPoint == a_rhs.m_priorPoint && m_nextPoint == a_rhs.m_nextPoint &&
85  m_cell == a_rhs.m_cell;
86  }
87 
90  int m_cell;
91 };
92 
93 typedef std::vector<CellData> VecCellData;
94 typedef std::vector<VecCellData> VecVecCellData;
95 typedef std::vector<VecVecCellData> VecVecVecCellData;
96 
98 class MeQuadBlossomImpl : public MeQuadBlossom
99 {
100 public:
101  MeQuadBlossomImpl(const VecPt3d& a_points, const VecInt2d& a_triangles);
102 
103  virtual int PreMakeQuads() override;
104  virtual BSHP<XmUGrid> MakeQuads(bool a_splitBoundaryPoints,
105  bool a_useAngle) override;
106 
107  virtual BSHP<XmUGrid> _MakeQuads(bool a_splitBoundaryPoints = true,
108  bool a_useAngle = false,
109  int cost = -10);
110  const VecPt3d& GetLocations() const;
111 
112  void PrepareForMatch(int a_cost,
113  bool a_useAngle);
114  VecInt MatchTriangles(bool a_splitBoundaryPoints);
115  void GetEdges(int a_cost);
116  void ProcessPointChains(int a_p,
117  VecVecCellData& a_chains,
118  int extra_cost,
119  VecEdge& a_interiorEdges,
120  VecEdge& a_boundaryEdges,
121  VecMeEdge& a_extraEdges,
122  VecInt2d& a_extraPoints);
123  void ExtractCellsAdjacentToPoint(VecVecCellData& a_pointFaceChains,
124  VecVecVecCellData& a_sortedFaceChains);
125  int FindFaceThatStartsWith(const VecCellData& facesData, int a_p);
126  int FindChainThatStartsWith(const VecVecCellData& a_chains, int a_p);
127  VecVecCellData SortIntoPointChains(VecCellData& faces);
128  VecMeEdge CalculateEdgeCost(bool a_useAngle);
129  VecInt2d EliminateEdges(const VecInt& eliminate);
130  VecEdge GetInteriorEdges(int a_p, const VecCellData& a_chain);
131  Edge GetBoundaryEdges(int a_p,
132  const VecCellData& a_chain,
133  int cost,
134  VecMeEdge& a_extraEdges,
135  VecInt2d& a_extraPoints);
136 
137 //private: // all public for testing
150 }; // class MeQuadBlossomImpl
151 
153 typedef std::pair<int, int> AdjPointMidpoint;
157 typedef std::vector<AdjPointMidpoint> VecAdjPointMidpoint;
160 typedef std::vector<VecAdjPointMidpoint> VecVecAdjPointMidpoint;
161 
162 //----- Internal functions -----------------------------------------------------
163 
164 //----- Class / Function definitions -------------------------------------------
165 
166 //------------------------------------------------------------------------------
170 //------------------------------------------------------------------------------
171 VecInt2d UGrid2dToVecInt2d(BSHP<XmUGrid> a_ugrid)
172 {
173  int numCells = a_ugrid->GetCellCount();
174  VecInt2d cells(numCells, VecInt());
175  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx)
176  {
177  VecInt& cell = cells[cellIdx];
178  if (a_ugrid->GetCellDimension(cellIdx) == 2 && a_ugrid->GetCellEdgeCount(cellIdx) == 3)
179  {
180  VecInt cellPoints = a_ugrid->GetCellPoints(cellIdx);
181  for (auto pointIdx : cellPoints)
182  {
183  cell.push_back(pointIdx);
184  }
185  }
186  }
187 
188  return cells;
189 } // UGrid2dToVecInt2d
190 //------------------------------------------------------------------------------
196 //------------------------------------------------------------------------------
197 BSHP<XmUGrid> BuildUGrid(const VecPt3d& a_points, const VecInt2d& a_faces)
198 {
199  VecInt cells;
200  for (auto& face : a_faces)
201  {
202  if (face.size() == 4)
203  {
204  cells.push_back(XMU_QUAD);
205  cells.push_back(4);
206  cells.insert(cells.end(), face.begin(), face.end());
207  }
208  else if (face.size() == 3)
209  {
210  cells.push_back(XMU_TRIANGLE);
211  cells.push_back(3);
212  cells.insert(cells.end(), face.begin(), face.end());
213  }
214  else
215  {
216  XM_ASSERT(0);
217  }
218  }
219 
220  return XmUGrid::New(a_points, cells);
221 } // BuildUGrid
222 //------------------------------------------------------------------------------
228 //------------------------------------------------------------------------------
229 double GetAngle(const Pt3d& p0, const Pt3d& pc, const Pt3d& p1)
230 {
231  Pt3d d0c = gmCreateVector(p0, pc);
232  Pt3d d1c = gmCreateVector(p1, pc);
233  double angle_c0 = atan2(d0c[1], d0c[0]);
234  double angle_c1 = atan2(d1c[1], d1c[0]);
235  double angle_0c1 = angle_c0 - angle_c1;
236  if (angle_0c1 < 0)
237  {
238  angle_0c1 += TWO_PI;
239  }
240  else if (angle_0c1 > TWO_PI)
241  {
242  angle_0c1 -= TWO_PI;
243  }
244 
245  return angle_0c1;
246 } // GetAngle
247 //------------------------------------------------------------------------------
257 //------------------------------------------------------------------------------
258 int GetEtaAngle(const Pt3d& p0, const Pt3d& p1, const Pt3d& p2, const Pt3d& p3)
259 {
260  double a0 = GetAngle(p1, p0, p2);
261  double a1 = GetAngle(p3, p1, p0);
262  double a2 = GetAngle(p0, p2, p3);
263  double a3 = GetAngle(p2, p3, p1);
264  if (a0 > XM_PI)
265  {
266  a0 = a0 - XM_PI;
267  }
268  if (a1 > XM_PI)
269  {
270  a1 = a1 - XM_PI;
271  }
272  if (a2 > XM_PI)
273  {
274  a2 = a2 - XM_PI;
275  }
276  if (a3 > XM_PI)
277  {
278  a3 = a3 - XM_PI;
279  }
280  double etaMax1 = std::max(fabs(PI_OVER_TWO - a0), fabs(PI_OVER_TWO - a1));
281  double etaMax2 = std::max(fabs(PI_OVER_TWO - a2), fabs(PI_OVER_TWO - a3));
282  double etaMax = 1.0 - TWO_OVER_PI * std::max(etaMax1, etaMax2);
283  double eta = std::max(0.0, etaMax);
284  return int(1000.0 * eta + 0.5);
285 } // GetEtaAngle
286 //------------------------------------------------------------------------------
296 //------------------------------------------------------------------------------
297 int GetEtaDistance(const Pt3d& p0, const Pt3d& p1, const Pt3d& p2, const Pt3d& p3)
298 {
299  double a = gmXyDistanceSquared(p1, p0);
300  double b = gmXyDistanceSquared(p0, p2);
301  double c = gmXyDistanceSquared(p1, p3);
302  double d = gmXyDistanceSquared(p3, p2);
303  double e = gmXyDistanceSquared(p1, p2);
304  double f = gmXyDistanceSquared(p0, p3);
305  //XM_ASSERT(a + b >= e);
306  //XM_ASSERT(c + d >= e);
307  //XM_ASSERT(a + c >= f);
308  //XM_ASSERT(b + d >= f);
309  double inveta1 = std::max((a + b) / e, e / (a + b));
310  double inveta2 = std::max((c + d) / e, e / (c + d));
311  double inveta3 = std::max((a + c) / f, f / (a + c));
312  double inveta4 = std::max((b + d) / f, f / (b + d));
313  double inveta = std::max(inveta1 + inveta2, inveta3 + inveta4);
314  //XM_ASSERT(inveta - (a + b + c + d)/std::min(e, f) < 1e-5);
315  return int(1000.0 * 2.0 / inveta + 0.5);
316 } // GetEtaDistance
317 //------------------------------------------------------------------------------
322 //------------------------------------------------------------------------------
323 void GetPointIdxsAttachedByEdge(BSHP<XmUGrid> a_ugrid, int a_pointIdx, VecInt& a_edgePoints)
324 {
325  // TODO: Use new UGrid code in xmsgrid
326  a_edgePoints.clear();
327  VecInt associatedCells = a_ugrid->GetPointAdjacentCells(a_pointIdx);
328  if (associatedCells.size() == 0)
329  {
330  return;
331  }
332  for (int i = 0; i < associatedCells.size(); ++i)
333  {
334  for (int j = 0; j < a_ugrid->GetCellEdgeCount(associatedCells[i]); ++j)
335  {
336  XmEdge temp = a_ugrid->GetCellEdge(associatedCells[i], j);
337  if (temp.GetFirst() == a_pointIdx)
338  {
339  a_edgePoints.push_back(temp.GetSecond());
340  }
341  else if (temp.GetSecond() == a_pointIdx)
342  {
343  a_edgePoints.push_back(temp.GetFirst());
344  }
345  }
346  }
347  std::sort(a_edgePoints.begin(), a_edgePoints.end());
348  auto it = std::unique(a_edgePoints.begin(), a_edgePoints.end());
349  a_edgePoints.erase(it, a_edgePoints.end());
350 } // XmUGridImpl::GetPointIdxsAttachedByEdge
351 //------------------------------------------------------------------------------
362 //------------------------------------------------------------------------------
363 int FindMidpoint(const VecVecAdjPointMidpoint& a_edgeMidsides, int a_idx0, int a_idx1)
364 {
365  if (a_idx0 > a_idx1)
366  std::swap(a_idx0, a_idx1);
367 
368  auto& adjPoints = a_edgeMidsides[a_idx0];
369  for (auto& adjPoint : adjPoints)
370  {
371  if (adjPoint.first == a_idx1)
372  {
373  return adjPoint.second;
374  }
375  }
376 
377  XM_ASSERT(0);
378  return XM_NONE;
379 } // FindMidpoint
380 
381 //------------------------------------------------------------------------------
383 //------------------------------------------------------------------------------
384 MeQuadBlossomImpl::MeQuadBlossomImpl(const VecPt3d& a_points, const VecInt2d& a_triangles)
385 : m_points(a_points)
386 , m_faces(a_triangles)
387 {
388 } // MeQuadBlossomImpl::MeQuadBlossomImpl
389 //------------------------------------------------------------------------------
396 //------------------------------------------------------------------------------
397 int MeQuadBlossomImpl::PreMakeQuads()
398 {
399  if (m_boundaryEdges.empty())
400  {
401  GetEdges(DEFAULT_COST);
402  }
403  return (int)m_boundaryEdges.size();
404 } // MeQuadBlossomImpl::PreMakeQuads
405 //------------------------------------------------------------------------------
414 //------------------------------------------------------------------------------
415 BSHP<XmUGrid> MeQuadBlossomImpl::MakeQuads(bool a_splitBoundaryPoints,
416  bool a_useAngle)
417 {
418  return _MakeQuads(a_splitBoundaryPoints, a_useAngle);
419 } // MeQuadBlossomImpl::MakeQuads
420 //------------------------------------------------------------------------------
430 //------------------------------------------------------------------------------
431 BSHP<XmUGrid> MeQuadBlossomImpl::_MakeQuads(bool a_splitBoundaryPoints /*= true */,
432  bool a_useAngle /*= false*/,
433  int a_cost /*= -10*/)
434 {
435  PrepareForMatch(a_cost, a_useAngle);
436  VecInt eliminate = MatchTriangles(a_splitBoundaryPoints);
437  EliminateEdges(eliminate);
438  BSHP<XmUGrid> ugrid = BuildUGrid(m_points, m_faces);
439  return ugrid;
440 } // MeQuadBlossomImpl::MakeQuads
441 //------------------------------------------------------------------------------
453 //------------------------------------------------------------------------------
454 void MeQuadBlossomImpl::PrepareForMatch(int a_cost,
455  bool a_useAngle)
456 {
457  if (m_boundaryEdges.empty())
458  {
459  GetEdges(a_cost);
460  }
461  else if (a_cost != DEFAULT_COST)
462  {
463  for (auto& edge : m_extraEdges)
464  {
465  edge.m_weight = a_cost;
466  }
467  }
468  m_costs = CalculateEdgeCost(a_useAngle);
469  m_costs.insert(m_costs.end(), m_extraEdges.begin(), m_extraEdges.end());
470 } // MeQuadBlossomImpl::PrepareForMatch
471 //------------------------------------------------------------------------------
481 //------------------------------------------------------------------------------
482 VecInt MeQuadBlossomImpl::MatchTriangles(bool a_splitBoundaryPoints)
483 {
484  bool maxCardinality = a_splitBoundaryPoints;
485  BSHP<MeWeightMatcher> matcher = MeWeightMatcher::New();
486  VecInt result = matcher->MatchWeights(m_costs, maxCardinality);
487 
488  // VecInt unmatched;
489  VecInt eliminate;
490  for (int i = 0; i < (int)result.size(); ++i)
491  {
492  int j = result[i];
493  // if (j != -1)
494  //{
495  // unmatched.push_back(i);
496  //}
497  // if j == -1, then j < i
498  if (j > i)
499  {
500  for (int k = 0; k < m_costs.size(); ++k)
501  {
502  if ((i == m_costs[k].m_f0 && j == m_costs[k].m_f1) ||
503  (i == m_costs[k].m_f1 && j == m_costs[k].m_f0))
504  {
505  eliminate.push_back(k);
506  }
507  }
508  }
509  }
510  return eliminate;
511 } // MeQuadBlossomImpl::MatchTriangles
512 //------------------------------------------------------------------------------
517 //------------------------------------------------------------------------------
518 void MeQuadBlossomImpl::GetEdges(int a_cost)
519 {
520  VecVecCellData dummy;
521  VecVecVecCellData sorted;
522  ExtractCellsAdjacentToPoint(dummy, sorted);
523 
524  int p = 0;
525  m_interiorEdges.clear();
526  m_boundaryEdges.clear();
527  m_extraEdges.clear();
528  m_extraPoints.clear();
529  m_splitPoints.clear();
530  for (auto& chains : sorted)
531  {
532  VecEdge interior, boundary;
533  ProcessPointChains(p, chains, a_cost, interior, boundary, m_extraEdges, m_extraPoints);
534  m_interiorEdges.insert(m_interiorEdges.begin(), interior.begin(), interior.end());
535  m_boundaryEdges.insert(m_boundaryEdges.begin(), boundary.begin(), boundary.end());
536  p += 1;
537  }
538 } // MeQuadBlossomImpl::GetEdges
539 //------------------------------------------------------------------------------
550 //------------------------------------------------------------------------------
551 void MeQuadBlossomImpl::ProcessPointChains(int a_p,
552  VecVecCellData& a_chains,
553  int extra_cost,
554  VecEdge& a_interiorEdges,
555  VecEdge& a_boundaryEdges,
556  VecMeEdge& a_extraEdges,
557  VecInt2d& a_extraPoints)
558 {
559  a_interiorEdges.clear();
560  a_boundaryEdges.clear();
561 
562  if (a_chains.size() == 0)
563  {
564  return;
565  }
566 
567  if (a_chains.size() == 1)
568  {
569  auto& chain = a_chains[0];
570  auto& first = chain[0];
571  auto& last = chain.back();
572  if (last.m_nextPoint == first.m_priorPoint) // This is an interior point
573  {
574  chain.push_back(first);
575  a_interiorEdges = GetInteriorEdges(a_p, chain);
576  return;
577  }
578  a_interiorEdges = GetInteriorEdges(a_p, chain);
579  Edge boundaryEdge = GetBoundaryEdges(a_p, chain, extra_cost, a_extraEdges, a_extraPoints);
580  a_boundaryEdges.push_back(boundaryEdge);
581  return;
582  }
583  // There is more than one chain, so we have possibly several groups of faces sharing the point
584  for (auto& chain : a_chains)
585  {
586  VecEdge edges = GetInteriorEdges(a_p, chain);
587  a_interiorEdges.insert(a_interiorEdges.begin(), edges.begin(), edges.end());
588  auto boundaryEdge = GetBoundaryEdges(a_p, chain, extra_cost, a_extraEdges, a_extraPoints);
589  a_boundaryEdges.push_back(boundaryEdge);
590  }
591 } // MeQuadBlossomImpl::ProcessVertexChains
592 //------------------------------------------------------------------------------
604 //------------------------------------------------------------------------------
605 void MeQuadBlossomImpl::ExtractCellsAdjacentToPoint(VecVecCellData& a_pointFaceChains,
606  VecVecVecCellData& a_sortedFaceChains)
607 {
608  a_pointFaceChains.clear();
609  a_pointFaceChains.resize(m_points.size(), VecCellData());
610  for (int fi = 0; fi < (int)m_faces.size(); ++fi)
611  {
612  VecInt face = m_faces[fi];
613 
614  int p0 = face[0];
615  int p1 = face[1];
616  int p2 = face[2];
617  a_pointFaceChains[p0].push_back(CellData(p2, p1, fi));
618  a_pointFaceChains[p1].push_back(CellData(p0, p2, fi));
619  a_pointFaceChains[p2].push_back(CellData(p1, p0, fi));
620  }
621 
622  a_sortedFaceChains.clear();
623  for (auto& pointFace : a_pointFaceChains)
624  {
625  VecVecCellData chains = SortIntoPointChains(pointFace);
626  a_sortedFaceChains.push_back(chains);
627  }
628 } // MeQuadBlossomImpl::ExtractCellsAdjacentToPoint
629 //------------------------------------------------------------------------------
634 //------------------------------------------------------------------------------
635 int MeQuadBlossomImpl::FindFaceThatStartsWith(const VecCellData& facesData, int a_p)
636 {
637  for (size_t i = 0; i < facesData.size(); ++i)
638  {
639  if (a_p == facesData[i].m_priorPoint)
640  {
641  return (int)i;
642  }
643  }
644  return XM_NONE;
645 } // MeQuadBlossomImpl::FindFaceThatStartsWith
646 //------------------------------------------------------------------------------
655 //------------------------------------------------------------------------------
656 int MeQuadBlossomImpl::FindChainThatStartsWith(const VecVecCellData& a_chains, int a_p)
657 {
658  for (size_t i = 0; i < a_chains.size(); ++i)
659  {
660  if (a_p == a_chains[i][0].m_priorPoint)
661  {
662  return (int)i;
663  }
664  }
665  return XM_NONE;
666 } // MeQuadBlossomImpl::FindChainThatStartsWith
667 //------------------------------------------------------------------------------
676 //------------------------------------------------------------------------------
677 VecVecCellData MeQuadBlossomImpl::SortIntoPointChains(VecCellData& facesData)
678 {
679  VecVecCellData chains;
680  VecCellData remaining = facesData;
681  int face_i = 0;
682  VecCellData chain;
683  while (remaining.size() > 0)
684  {
685  auto iter = remaining.begin() + face_i;
686  CellData face = remaining[face_i];
687  remaining.erase(iter);
688  int p = face.m_nextPoint;
689  chain.push_back(face);
690  face_i = FindFaceThatStartsWith(remaining, p);
691  if (face_i == XM_NONE)
692  {
693  face_i = 0;
694  // Now search chains for a chain that begins with p
695  int chain_i = FindChainThatStartsWith(chains, p);
696  if (chain_i != XM_NONE)
697  {
698  // insert chain at the front of chains[chain_i]
699  chains[chain_i].insert(chains[chain_i].begin(), chain.begin(), chain.end());
700  }
701  else
702  {
703  chains.push_back(chain);
704  }
705  chain.clear();
706  }
707  }
708  return chains;
709 } // MeQuadBlossomImpl::SortIntoVertexChains
710 //------------------------------------------------------------------------------
716 //------------------------------------------------------------------------------
717 VecMeEdge MeQuadBlossomImpl::CalculateEdgeCost(bool a_useAngle)
718 {
719  auto& eta = a_useAngle ? GetEtaAngle : GetEtaDistance;
720  VecMeEdge costs;
721  // Note: m_interiorEdges contains only one of each edge and its twin, not both
722  for (auto& edge : m_interiorEdges)
723  {
724  // p1 and p2 are the points of the edge
725  const Pt3d& p1 = m_points[edge.m_p0];
726  const Pt3d& p2 = m_points[edge.m_p1];
727 
728  // p0 and p3 are the opposite points from the edge in the respective faces of the edge
729  // If those faces are faces, they are the triangle points not on the edge.
730  const Pt3d& p0 = m_points[edge.m_pL];
731  const Pt3d& p3 = m_points[edge.m_pR];
732 
733  int cost = int(eta(p0, p1, p2, p3));
734  costs.push_back({edge.m_fL, edge.m_fR, cost});
735  }
736  return costs;
737 } // MeQuadBlossomImpl::CalculateEdgeCost
738 //------------------------------------------------------------------------------
752 //------------------------------------------------------------------------------
753 VecInt2d MeQuadBlossomImpl::EliminateEdges(const VecInt& eliminate)
754 {
755  int nInteriorEdges = (int)m_interiorEdges.size();
756 
757  VecInt splitPointsRedir(m_points.size(), 0);
758  std::iota(splitPointsRedir.begin(), splitPointsRedir.end(), 0);
759  for (auto i : eliminate)
760  {
761  if (i >= nInteriorEdges)
762  {
763  // split the point
764  //
765  // if this splits, it will produce quads:
766  // for first.m_cell: { first.m_nextPoint, first.m_priorPoint, p, px }
767  // for last.m_cell: { p, last.m_nextPoint, last.m_priorPoint, px }
768  //
769  // first.m_nextPoint \ px / last.m_priorPoint
770  // \ /
771  // first.m_cell \ / last.m_cell
772  // \ /
773  // first.m_priorPoint ---------- p -------- last.m_nextPoint
774  //
775  int j = i - nInteriorEdges;
776  const auto& extra = m_extraPoints[j];
777  const auto& edge = m_costs[i];
778  int p = extra[0];
779  int firstVertexOut = extra[1];
780  int firstVertexIn = extra[2];
781  int lastVertexOut = extra[3];
782  int lastVertexIn = extra[4];
783  int first = edge.m_f0;
784  int last = edge.m_f1;
785  int px = (int)m_points.size();
786  m_points.push_back(m_splitPoints[j]);
787  m_faces[first] = { firstVertexOut, firstVertexIn, p, px };
788  m_faces[last] = { p, lastVertexOut, lastVertexIn, px };
789 
790  splitPointsRedir[p] = px;
791  }
792  }
793 
794  for (auto i : eliminate)
795  {
796  if (i < nInteriorEdges)
797  {
798  // eliminate the edge
799  const Edge& edge = m_interiorEdges[i];
800  int p0 = splitPointsRedir[edge.m_p0];
801  int pR = splitPointsRedir[edge.m_pR];
802  int p1 = splitPointsRedir[edge.m_p1];
803  int pL = splitPointsRedir[edge.m_pL];
804  m_faces[edge.m_fL] = {p0, pR, p1, pL};
805  m_faces[edge.m_fR].clear();
806  }
807  }
808 
809  VecInt2d tempFaces;
810  for (auto& face : m_faces)
811  {
812  if (!face.empty())
813  {
814  tempFaces.push_back(face);
815  }
816  }
817 
818  m_faces = tempFaces;
819  return tempFaces;
820 } // MeQuadBlossomImpl::EliminateEdges
821 //------------------------------------------------------------------------------
828 //------------------------------------------------------------------------------
829 VecEdge MeQuadBlossomImpl::GetInteriorEdges(int a_p, const VecCellData& chain)
830 {
831  VecEdge edges;
832  XM_ASSERT(chain.size() > 0);
833  for (size_t i = 0; i < chain.size() - 1; ++i)
834  {
835  const CellData& last = chain[i];
836  const CellData& face = chain[i + 1];
837  int p1 = face.m_priorPoint;
838  // only create an edge if p1 > a_p. Let the chain of p1 create it otherwise.
839  if (p1 > a_p)
840  {
841  // last: [m_priorPoint=2, m_nextPoint=5, face=3],
842  // current: [m_priorPoint=5, m_nextPoint=8, face=6],
843  // a_p: 4=> [p0=4, p1=5, fL=3, fR=6, pL=2, pR=8]
844 
845  // last: [m_priorPoint=3, m_nextPoint=0, face=0],
846  // current: [m_priorPoint=0, m_nextPoint=1, face=1], a_p: 4
847  // => [p0=4, p1=0, fL=0, fR=1, pL=3, pR=1]
848  edges.push_back({a_p, p1, last.m_cell, face.m_cell, last.m_priorPoint, face.m_nextPoint});
849  }
850  }
851  return edges;
852 } // MeQuadBlossomImpl::GetInteriorEdges
853 //------------------------------------------------------------------------------
863 //------------------------------------------------------------------------------
864 Edge MeQuadBlossomImpl::GetBoundaryEdges(int a_p,
865  const VecCellData& chain,
866  int cost,
867  VecMeEdge& a_extraEdges,
868  VecInt2d& a_extraPoints)
869 {
870  // a_p = 5
871  const CellData& last = chain.back(); // [4, 3, 0] => [5, 3, 0, None, 4, None]
872  Edge edge = {a_p, last.m_nextPoint, last.m_cell, XM_NONE, last.m_priorPoint, XM_NONE};
873 
874  if (chain.size() > 2)
875  {
876  const CellData& first = chain[0]; // [m_priorPoint=1, m_nextPoint=7, face=6]
877  a_extraEdges.push_back({first.m_cell, last.m_cell, cost}); // [6, 0, cost]
878  // if this splits, it will produce quads:
879  // for first.m_cell: { a_p, first.m_nextPoint, first.m_priorPoint, p' }
880  // for last.m_cell: { last.m_nextPoint, last.m_priorPoint, a_p, p' }
881  //
882  // last.m_nextP \ / first.m_priorP
883  // \ p' /
884  // last.m_cell \ / first.m_cell
885  // last.m_priorP ---------- a_p -------- first.m_nextP
886  a_extraPoints.push_back(
887  {a_p, first.m_nextPoint, first.m_priorPoint, last.m_nextPoint, last.m_priorPoint});
888  // compute p'
889  Pt3d px;
890  int nTris = (int)chain.size();
891  const CellData& tri = chain[nTris >> 1];
892  const Pt3d& p0 = m_points[a_p];
893  const Pt3d& p1 = m_points[tri.m_priorPoint];
894  if (nTris & 0x1)
895  {
896  // There are an odd number of faces, so use the centroid of the
897  // middle triangle.
898  const Pt3d& p2 = m_points[tri.m_nextPoint];
899  px = (p0 + p1 + p2) / 3.0;
900  }
901  else
902  {
903  // There are an even number of faces, so use the middle triangle's
904  // incoming edge.
905  px = (p0 + p1) / 2.0;
906  }
907  m_splitPoints.push_back(px);
908  }
909  return edge;
910 
911 } // MeQuadBlossomImpl::GetBoundaryEdges
912 
913 } // namespace
914 
920 //------------------------------------------------------------------------------
924 //------------------------------------------------------------------------------
925 BSHP<MeQuadBlossom> MeQuadBlossom::New(BSHP<XmUGrid> a_ugrid)
926 {
927  const VecPt3d& points = a_ugrid->GetLocations();
928  VecInt2d triangles = UGrid2dToVecInt2d(a_ugrid);
929  BSHP<MeQuadBlossom> wm(new MeQuadBlossomImpl(points, triangles));
930  return wm;
931 } // MeQuadBlossom::New
932 //------------------------------------------------------------------------------
934 //------------------------------------------------------------------------------
936 {
937 } // MeQuadBlossom::MeQuadBlossom
938 //------------------------------------------------------------------------------
940 //------------------------------------------------------------------------------
942 {
943 } // MeQuadBlossom::~MeQuadBlossom
944 //------------------------------------------------------------------------------
948 //------------------------------------------------------------------------------
950 {
951  double minutes = 6.8E-11 * a_numPoints * a_numPoints * a_numPoints;
952  return minutes;
953 } // MeQuadBlossom::EstimatedRunTimeInMinutes
954 //------------------------------------------------------------------------------
959 //------------------------------------------------------------------------------
960 BSHP<XmUGrid> MeQuadBlossom::SplitToQuads(BSHP<XmUGrid> a_ugrid)
961 {
962  VecPt3d points = a_ugrid->GetLocations();
963  VecInt cells;
964 
965  int numPoints = a_ugrid->GetPointCount();
966  VecVecAdjPointMidpoint edgeMidsides(numPoints);
967  for (int pointIdx = 0; pointIdx < numPoints; ++pointIdx)
968  {
969  const Pt3d p0 = points[pointIdx];
970  VecInt adjPoints;
971  GetPointIdxsAttachedByEdge(a_ugrid, pointIdx, adjPoints);
972  VecAdjPointMidpoint adjPoints2;
973  for (auto otherIdx : adjPoints)
974  {
975  if (otherIdx > pointIdx)
976  {
977  const Pt3d& p1 = points[otherIdx];
978  Pt3d midPoint = (p0 + p1) * 0.5;
979  int midIdx = (int)points.size();
980  points.push_back(midPoint);
981  adjPoints2.push_back({ otherIdx, midIdx });
982  }
983  }
984  // sort adjPoint2 by element 0
985  std::sort(adjPoints2.begin(), adjPoints2.end());
986  edgeMidsides[pointIdx] = adjPoints2;
987  }
988 
989  // also get centroids and add to new points
990  // track start of new centroid starts
991  int numCells = a_ugrid->GetCellCount();
992  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx)
993  {
994  // TODO: Use new UGrid centroid code in xmsgrid
995  VecInt cellPointIdxs = a_ugrid->GetCellPoints(cellIdx);
996  VecPt3d cellPoints = a_ugrid->GetPointsLocations(cellPointIdxs);
997  Pt3d centroid = gmComputeCentroid(cellPoints);
998  int centroidIdx = (int)points.size();
999  points.push_back(centroid);
1000  int numCellPoints = (int)cellPointIdxs.size();
1001  int lastIdx = cellPointIdxs.back();
1002  int midsideLast = FindMidpoint(edgeMidsides, cellPointIdxs.front(), lastIdx);
1003  for (size_t i = 0; i < numCellPoints; ++i)
1004  {
1005  int currentIdx = cellPointIdxs[i];
1006  int nextIdx = cellPointIdxs[(i + 1) % numCellPoints];
1007  int midside = FindMidpoint(edgeMidsides, currentIdx, nextIdx);
1008 
1012 
1013  cells.push_back(XMU_QUAD);
1014  cells.push_back(4);
1015  cells.push_back(currentIdx);
1016  cells.push_back(midside);
1017  cells.push_back(centroidIdx);
1018  cells.push_back(midsideLast);
1019 
1020  midsideLast = midside;
1021  }
1022  }
1023 
1024  return XmUGrid::New(points, cells);
1025 } // MeQuadBlossom::SplitToQuads
1026 
1027 } // namespace xms
1028 
1029 #if CXX_TEST
1030 // UNIT TESTS
1033 
1035 
1036 #include <xmscore/testing/TestTools.h>
1038 
1039 //----- Namespace declaration --------------------------------------------------
1040 
1041 using namespace xms;
1042 
1047 //------------------------------------------------------------------------------
1049 //------------------------------------------------------------------------------
1051 {
1052  VecInt2d triangles;
1053  VecPt3d points;
1054 
1055  MeQuadBlossomImpl blossom(points, triangles);
1056 
1057  int p = 4;
1058  VecCellData chain = {{3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}, {7, 3, 7}};
1059  VecEdge edges = blossom.GetInteriorEdges(p, chain);
1060 
1061  VecEdge expect = {{4, 5, 3, 6, 2, 8}, {4, 8, 6, 5, 5, 7}, {4, 7, 5, 7, 8, 3}};
1062 
1063  TS_ASSERT_EQUALS(expect, edges);
1064 
1065  chain = {{3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}, {7, 3, 7}, {3, 0, 0}};
1066  edges = blossom.GetInteriorEdges(p, chain);
1067 
1068  // shouldn't chain result because last interior edge does not have increasing point indices;
1069  TS_ASSERT_EQUALS(expect, edges);
1070 
1071  chain = {{7, 3, 7}, {3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}, {7, 3, 7}};
1072  edges = blossom.GetInteriorEdges(p, chain);
1073 
1074  expect = {{4, 5, 3, 6, 2, 8}, {4, 8, 6, 5, 5, 7}, {4, 7, 5, 7, 8, 3}};
1075  TS_ASSERT_EQUALS(expect, edges);
1076 
1077  chain = {{7, 3, 7}, {3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}};
1078  edges = blossom.GetInteriorEdges(p, chain);
1079 
1080  expect = {{4, 5, 3, 6, 2, 8}, {4, 8, 6, 5, 5, 7}};
1081  TS_ASSERT_EQUALS(expect, edges);
1082 
1083  p = 5;
1084  chain = {{8, 4, 6}, {4, 2, 3}};
1085  edges = blossom.GetInteriorEdges(p, chain);
1086  expect = {};
1087  TS_ASSERT_EQUALS(expect, edges);
1088 
1089  p = 2;
1090  chain = {{5, 4, 3}, {4, 1, 2}};
1091  expect = {{2, 4, 3, 2, 5, 1}};
1092  edges = blossom.GetInteriorEdges(p, chain);
1093  TS_ASSERT_EQUALS(expect, edges);
1094 } // MeQuadBlossomUnitTests::testGetInteriorEdges
1095 //------------------------------------------------------------------------------
1097 //------------------------------------------------------------------------------
1099 {
1100  VecInt2d triangles;
1101  VecPt3d points(8, Pt3d());
1102  MeQuadBlossomImpl blossom(points, triangles);
1103 
1104  int p = 0;
1105  VecCellData chain = {{1, 4, 1}, {4, 3, 0}};
1106  int cost = -1000;
1107  VecMeEdge extraEdges;
1108  VecInt2d extraPoints;
1109  Edge result = blossom.GetBoundaryEdges(p, chain, cost, extraEdges, extraPoints);
1110  Edge expect = {0, 3, 0, XM_NONE, 4, XM_NONE};
1111 
1112  TS_ASSERT_EQUALS(expect, result);
1113  TS_ASSERT(extraEdges.empty());
1114  TS_ASSERT(extraPoints.empty());
1115 
1116  p = 3;
1117  chain = {{0, 4, 0}, {4, 7, 7}, {7, 6, 4}};
1118  cost = -91;
1119  points.resize(8);
1120  result = blossom.GetBoundaryEdges(p, chain, cost, extraEdges, extraPoints);
1121  expect = {3, 6, 4, XM_NONE, 7, XM_NONE};
1122  VecMeEdge expectExtra = {{0, 4, -91}};
1123  VecInt2d expectExtraVertices = {{3, 4, 0, 6, 7}};
1124 
1125  TS_ASSERT_EQUALS(expect, result);
1126  TS_ASSERT_EQUALS(expectExtra, extraEdges);
1127  TS_ASSERT_EQUALS(expectExtraVertices, extraPoints);
1128 
1129  p = 6;
1130  chain = {{3, 7, 4}};
1131  extraEdges.clear();
1132  extraPoints.clear();
1133  points.resize(8);
1134  result = blossom.GetBoundaryEdges(p, chain, cost, extraEdges, extraPoints);
1135  expect = {6, 7, 4, XM_NONE, 3, XM_NONE};
1136  expectExtraVertices = {};
1137 
1138  TS_ASSERT_EQUALS(expect, result);
1139  TS_ASSERT(extraEdges.empty());
1140  TS_ASSERT(extraPoints.empty());
1141 
1142  p = 7;
1143  chain = {{4, 8, 5}};
1144  points.resize(8);
1145  result = blossom.GetBoundaryEdges(p, chain, cost, extraEdges, extraPoints);
1146  expect = {7, 8, 5, XM_NONE, 4, XM_NONE};
1147 
1148  TS_ASSERT_EQUALS(expect, result);
1149  TS_ASSERT(extraEdges.empty());
1150  TS_ASSERT(extraPoints.empty());
1151 
1152  chain = {{6, 3, 4}};
1153  points.resize(8);
1154  result = blossom.GetBoundaryEdges(p, chain, cost, extraEdges, extraPoints);
1155  expect = {7, 3, 4, XM_NONE, 6, XM_NONE};
1156 
1157  TS_ASSERT_EQUALS(expect, result);
1158  TS_ASSERT(extraEdges.empty());
1159  TS_ASSERT(extraPoints.empty());
1160 } // MeQuadBlossomUnitTests::testGetBoundaryEdges
1161 //------------------------------------------------------------------------------
1163 //------------------------------------------------------------------------------
1165 {
1166  VecInt2d triangles;
1167  VecPt3d points;
1168  MeQuadBlossomImpl blossom(points, triangles);
1169 
1170  int p = 4;
1171  VecVecCellData chains = {
1172  {{7, 3, 7}, {3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}}};
1173  int extraCost = -1000;
1174  VecEdge interiorEdges;
1175  VecEdge boundaryEdges;
1176  VecMeEdge extraEdges;
1177  VecInt2d extraPoints;
1178  blossom.ProcessPointChains(p, chains, extraCost, interiorEdges, boundaryEdges, extraEdges,
1179  extraPoints);
1180  VecEdge expectInterior = {{4, 5, 3, 6, 2, 8}, {4, 8, 6, 5, 5, 7}, {4, 7, 5, 7, 8, 3}};
1181  VecEdge expectBoundary;
1182  VecMeEdge expectExtra;
1183  VecInt2d expectExtraVertices;
1184 
1185  TS_ASSERT_EQUALS(expectInterior, interiorEdges);
1186  TS_ASSERT_EQUALS(expectBoundary, boundaryEdges);
1187  TS_ASSERT_EQUALS(expectExtra, extraEdges);
1188  TS_ASSERT_EQUALS(expectExtraVertices, extraPoints);
1189 
1190  p = 6;
1191  chains = {{{3, 7, 4}}};
1192  interiorEdges.clear();
1193  boundaryEdges.clear();
1194  extraEdges.clear();
1195  extraPoints.clear();
1196  blossom.ProcessPointChains(p, chains, extraCost, interiorEdges, boundaryEdges, extraEdges,
1197  extraPoints);
1198  expectInterior = {};
1199  expectBoundary = {{6, 7, 4, XM_NONE, 3, XM_NONE}};
1200  expectExtra = {};
1201  expectExtraVertices = {};
1202 
1203  TS_ASSERT_EQUALS(expectInterior, interiorEdges);
1204  TS_ASSERT_EQUALS(expectBoundary, boundaryEdges);
1205  TS_ASSERT_EQUALS(expectExtra, extraEdges);
1206  TS_ASSERT_EQUALS(expectExtraVertices, extraPoints);
1207 
1208  p = 5;
1209  chains = {{{8, 4, 6}, {4, 2, 3}}};
1210  interiorEdges.clear();
1211  boundaryEdges.clear();
1212  extraEdges.clear();
1213  extraPoints.clear();
1214  blossom.ProcessPointChains(p, chains, extraCost, interiorEdges, boundaryEdges, extraEdges,
1215  extraPoints);
1216  expectInterior = {};
1217  expectBoundary = {{5, 2, 3, XM_NONE, 4, XM_NONE}};
1218  expectExtra = {};
1219  expectExtraVertices = {};
1220 
1221  TS_ASSERT_EQUALS(expectInterior, interiorEdges);
1222  TS_ASSERT_EQUALS(expectBoundary, boundaryEdges);
1223  TS_ASSERT_EQUALS(expectExtra, extraEdges);
1224  TS_ASSERT_EQUALS(expectExtraVertices, extraPoints);
1225 
1226  p = 3;
1227  chains = {{{0, 4, 0}}, {{7, 13, 8}, {13, 6, 4}}};
1228  extraCost = 13;
1229  interiorEdges.clear();
1230  boundaryEdges.clear();
1231  extraEdges.clear();
1232  extraPoints.clear();
1233  blossom.ProcessPointChains(p, chains, extraCost, interiorEdges, boundaryEdges, extraEdges,
1234  extraPoints);
1235  expectInterior = {{3, 13, 8, 4, 7, 6}};
1236  expectBoundary = {{3, 4, 0, XM_NONE, 0, XM_NONE}, {3, 6, 4, XM_NONE, 13, XM_NONE}};
1237  expectExtra = {};
1238  expectExtraVertices = {};
1239 
1240  TS_ASSERT_EQUALS(expectInterior, interiorEdges);
1241  TS_ASSERT_EQUALS(expectBoundary, boundaryEdges);
1242  TS_ASSERT_EQUALS(expectExtra, extraEdges);
1243  TS_ASSERT_EQUALS(expectExtraVertices, extraPoints);
1244 } // MeQuadBlossomUnitTests::testProcessVertexChains
1245 //------------------------------------------------------------------------------
1247 //------------------------------------------------------------------------------
1249 {
1250  double h = 10.0 * sqrt(2.0);
1251  Pt3d p1 = {0, -h};
1252  Pt3d p2 = {0, h};
1253  Pt3d p0 = {-h, 0};
1254  Pt3d p3 = {h, 0};
1255 
1256  int angle_cost = GetEtaAngle(p0, p1, p2, p3);
1257  TS_ASSERT_EQUALS(1000, angle_cost);
1258  int distance_cost = GetEtaDistance(p0, p1, p2, p3);
1259  TS_ASSERT_EQUALS(1000, distance_cost);
1260 
1261  double a = XM_PI / 3.0;
1262  double hy = 10.0 * sin(a);
1263  double hx = 10 * cos(a);
1264  double a_d = a * 180.0 / XM_PI;
1265  p1 = {0, -hy};
1266  p2 = {0, hy};
1267  p0 = {-hx, 0};
1268  p3 = {hx, 0};
1269 
1270  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1271  TS_ASSERT_EQUALS(667, angle_cost);
1272  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1273  TS_ASSERT_EQUALS(500, distance_cost);
1274 
1275  angle_cost = GetEtaAngle(p1, p0, p3, p2);
1276  TS_ASSERT_EQUALS(667, angle_cost);
1277  distance_cost = GetEtaDistance(p1, p0, p3, p2);
1278  TS_ASSERT_EQUALS(500, distance_cost);
1279 
1280  a = XM_PI / 4.0;
1281  hy = 10.0 * sin(a);
1282  hx = 10.0 * cos(a);
1283  a_d = a * 180.0 / XM_PI;
1284  p1 = {0, -hy};
1285  p2 = {0, hy};
1286  p0 = {-hx, 0};
1287  p3 = {hx, 0};
1288 
1289  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1290  TS_ASSERT_EQUALS(1000, angle_cost);
1291  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1292  TS_ASSERT_EQUALS(1000, distance_cost);
1293 
1294  a = XM_PI / 6.0;
1295  hy = 10.0 * sin(a);
1296  hx = 10.0 * cos(a);
1297  a_d = a * 180.0 / XM_PI;
1298  p1 = {0, -hy};
1299  p2 = {0, hy};
1300  p0 = {-hx, 0};
1301  p3 = {hx, 0};
1302 
1303  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1304  TS_ASSERT_EQUALS(667, angle_cost);
1305  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1306  TS_ASSERT_EQUALS(500, distance_cost);
1307 
1308  a = XM_PI / 8.0;
1309  hy = 10.0 * sin(a);
1310  hx = 10.0 * cos(a);
1311  a_d = a * 180.0 / XM_PI;
1312  p1 = {0, -hy};
1313  p2 = {0, hy};
1314  p0 = {-hx, 0};
1315  p3 = {hx, 0};
1316 
1317  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1318  TS_ASSERT_EQUALS(500, angle_cost);
1319  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1320  TS_ASSERT_EQUALS(293, distance_cost);
1321 
1322  angle_cost = GetEtaAngle(p1, p0, p3, p2);
1323  TS_ASSERT_EQUALS(500, angle_cost);
1324  distance_cost = GetEtaDistance(p1, p0, p3, p2);
1325  TS_ASSERT_EQUALS(293, distance_cost);
1326 
1327  a = XM_PI / 12.0;
1328  hy = sin(a);
1329  hx = cos(a);
1330  a_d = a * 180.0 / XM_PI;
1331  p1 = {0, -hy};
1332  p2 = {0, hy};
1333  p0 = {-hx, 0};
1334  p3 = {hx, 0};
1335 
1336  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1337  TS_ASSERT_EQUALS(333, angle_cost);
1338  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1339  TS_ASSERT_EQUALS(134, distance_cost);
1340 
1341  angle_cost = GetEtaAngle(p1, p0, p3, p2);
1342  TS_ASSERT_EQUALS(333, angle_cost);
1343  distance_cost = GetEtaDistance(p1, p0, p3, p2);
1344  TS_ASSERT_EQUALS(134, distance_cost);
1345 
1346  // Larger scale does not matter. Relative scale gives same result.
1347  p1 = {0, -hy * 100.0};
1348  p2 = {0, hy * 100.0};
1349  p0 = {-hx * 100.0, 0};
1350  p3 = {hx * 100.0, 0};
1351 
1352  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1353  TS_ASSERT_EQUALS(333, angle_cost);
1354  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1355  TS_ASSERT_EQUALS(134, distance_cost);
1356 
1357  angle_cost = GetEtaAngle(p1, p0, p3, p2);
1358  TS_ASSERT_EQUALS(333, angle_cost);
1359  distance_cost = GetEtaDistance(p1, p0, p3, p2);
1360  TS_ASSERT_EQUALS(134, distance_cost);
1361 
1362  // Smaller scale does not matter. Relative scale gives same result.
1363  p1 = {0, -hy / 100.0};
1364  p2 = {0, hy / 100.0};
1365  p0 = {-hx / 100.0, 0};
1366  p3 = {hx / 100.0, 0};
1367 
1368  angle_cost = GetEtaAngle(p0, p1, p2, p3);
1369  TS_ASSERT_EQUALS(333, angle_cost);
1370  distance_cost = GetEtaDistance(p0, p1, p2, p3);
1371  TS_ASSERT_EQUALS(134, distance_cost);
1372 
1373  angle_cost = GetEtaAngle(p1, p0, p3, p2);
1374  TS_ASSERT_EQUALS(333, angle_cost);
1375  distance_cost = GetEtaDistance(p1, p0, p3, p2);
1376  TS_ASSERT_EQUALS(134, distance_cost);
1377 } // MeQuadBlossomUnitTests::testEta
1378 //------------------------------------------------------------------------------
1380 //------------------------------------------------------------------------------
1382 {
1383  Pt3d pc = {17, -31};
1384  Pt3d p0 = {17 + 8, -31};
1385  Pt3d p1 = {17 + 8, -31 + 0};
1386  double angle = GetAngle(p0, pc, p1);
1387  TS_ASSERT_DELTA(0.0, angle, 1.0e-5);
1388  angle = GetAngle(p1, pc, p0);
1389  TS_ASSERT_DELTA(0.0, angle, 1.0e-5);
1390  p1 = {17 + 8, -31 + 8};
1391  angle = GetAngle(p0, pc, p1);
1392  TS_ASSERT_DELTA(XM_PI * 1.75, angle, 1.0e-5);
1393  angle = GetAngle(p1, pc, p0);
1394  TS_ASSERT_DELTA(XM_PI * 0.25, angle, 1.0e-5);
1395  p1 = {17 + 0, -31 + 8};
1396  angle = GetAngle(p0, pc, p1);
1397  TS_ASSERT_DELTA(XM_PI * 1.5, angle, 1.0e-5);
1398  angle = GetAngle(p1, pc, p0);
1399  TS_ASSERT_DELTA(XM_PI * 0.5, angle, 1.0e-5);
1400  p1 = {17 - 8, -31 + 8};
1401  angle = GetAngle(p0, pc, p1);
1402  TS_ASSERT_DELTA(XM_PI * 1.25, angle, 1.0e-5);
1403  angle = GetAngle(p1, pc, p0);
1404  TS_ASSERT_DELTA(XM_PI * 0.75, angle, 1.0e-5);
1405  p1 = {17 - 8, -31 + 0};
1406  angle = GetAngle(p0, pc, p1);
1407  TS_ASSERT_DELTA(XM_PI, angle, 1.0e-5);
1408  angle = GetAngle(p1, pc, p0);
1409  TS_ASSERT_DELTA(XM_PI, angle, 1.0e-5);
1410  p1 = {17 - 8, -31 - 8};
1411  angle = GetAngle(p0, pc, p1);
1412  TS_ASSERT_DELTA(XM_PI * 0.75, angle, 1.0e-5);
1413  angle = GetAngle(p1, pc, p0);
1414  TS_ASSERT_DELTA(XM_PI * 1.25, angle, 1.0e-5);
1415  p1 = {17 - 0, -31 - 8};
1416  angle = GetAngle(p0, pc, p1);
1417  TS_ASSERT_DELTA(XM_PI * 0.5, angle, 1.0e-5);
1418  angle = GetAngle(p1, pc, p0);
1419  TS_ASSERT_DELTA(XM_PI * 1.5, angle, 1.0e-5);
1420  p1 = {17 + 8, -31 - 8};
1421  angle = GetAngle(p0, pc, p1);
1422  TS_ASSERT_DELTA(XM_PI * 0.25, angle, 1.0e-5);
1423  angle = GetAngle(p1, pc, p0);
1424  TS_ASSERT_DELTA(XM_PI * 1.75, angle, 1.0e-5);
1425 } // MeQuadBlossomUnitTests::testGetAngle
1426 //------------------------------------------------------------------------------
1428 //------------------------------------------------------------------------------
1430 {
1431  VecInt2d triangles = {{0, 3, 4}, {0, 4, 1}, {1, 4, 2}, {2, 4, 5},
1432  {3, 6, 7}, {4, 7, 8}, {4, 8, 5}, {3, 7, 4}};
1433  VecPt3d points(9, Pt3d());
1434 
1435  MeQuadBlossomImpl blossom(points, triangles);
1436  VecVecCellData vertexFaces;
1437  VecVecVecCellData sortedFacesChains;
1438  blossom.ExtractCellsAdjacentToPoint(vertexFaces, sortedFacesChains);
1439 
1440  VecVecCellData expected = {
1441  {{4, 3, 0}, {1, 4, 1}},
1442  {{4, 0, 1}, {2, 4, 2}},
1443  {{4, 1, 2}, {5, 4, 3}},
1444  {{0, 4, 0}, {7, 6, 4}, {4, 7, 7}},
1445  {{3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {8, 7, 5}, {5, 8, 6}, {7, 3, 7}},
1446  {{4, 2, 3}, {8, 4, 6}},
1447  {{3, 7, 4}},
1448  {{6, 3, 4}, {4, 8, 5}, {3, 4, 7}},
1449  {{7, 4, 5}, {4, 5, 6}}};
1450 
1451  VecVecVecCellData expectedSorted = {
1452  {{{1, 4, 1}, {4, 3, 0}}},
1453  {{{2, 4, 2}, {4, 0, 1}}},
1454  {{{5, 4, 3}, {4, 1, 2}}},
1455  {{{0, 4, 0}, {4, 7, 7}, {7, 6, 4}}},
1456  {{{3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}, {7, 3, 7}}},
1457  {{{8, 4, 6}, {4, 2, 3}}},
1458  {{{3, 7, 4}}},
1459  {{{6, 3, 4}, {3, 4, 7}, {4, 8, 5}}},
1460  {{{7, 4, 5}, {4, 5, 6}}}};
1461 
1462  TS_ASSERT_EQUALS(expected, vertexFaces);
1463  TS_ASSERT_EQUALS(expectedSorted, sortedFacesChains);
1464 
1465  triangles = {{0, 3, 4}, {0, 4, 1}, {1, 4, 2}, {2, 4, 5}, {3, 6, 7}, {4, 7, 8}, {4, 8, 5}};
1466 
1467  MeQuadBlossomImpl blossom2(points, triangles);
1468  blossom2.ExtractCellsAdjacentToPoint(vertexFaces, sortedFacesChains);
1469 
1470  expected = {{{4, 3, 0}, {1, 4, 1}},
1471  {{4, 0, 1}, {2, 4, 2}},
1472  {{4, 1, 2}, {5, 4, 3}},
1473  {{0, 4, 0}, {7, 6, 4}},
1474  {{3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {8, 7, 5}, {5, 8, 6}},
1475  {{4, 2, 3}, {8, 4, 6}},
1476  {{3, 7, 4}},
1477  {{6, 3, 4}, {4, 8, 5}},
1478  {{7, 4, 5}, {4, 5, 6}}};
1479 
1480  // p: {p, p01=p10, f1, f0, f
1481  // 0: {0, 4, 0, 1, 3, 1} boundary: 1, 0, 3: {0, 3, 0, 4}, {1, 0, 1, 4}
1482  // 1: {1, 4, 1, 2, 0, 2} boundary:
1483  expectedSorted = {{{{1, 4, 1}, {4, 3, 0}}},
1484  {{{2, 4, 2}, {4, 0, 1}}},
1485  {{{5, 4, 3}, {4, 1, 2}}},
1486  {{{0, 4, 0}}, {{7, 6, 4}}},
1487  {{{3, 0, 0}, {0, 1, 1}, {1, 2, 2}, {2, 5, 3}, {5, 8, 6}, {8, 7, 5}}},
1488  {{{8, 4, 6}, {4, 2, 3}}},
1489  {{{3, 7, 4}}},
1490  {{{6, 3, 4}}, {{4, 8, 5}}},
1491  {{{7, 4, 5}, {4, 5, 6}}}};
1492 
1493  TS_ASSERT_EQUALS(expected, vertexFaces);
1494  TS_ASSERT_EQUALS(expectedSorted, sortedFacesChains);
1495 } // MeQuadBlossomUnitTests::testExtractCellsAdjacentToPoint
1496 //------------------------------------------------------------------------------
1498 //------------------------------------------------------------------------------
1500 {
1501  {
1502  // single triangle
1503  VecInt2d triangles = {{2, 9, 6}};
1504  VecPt3d points(10, Pt3d());
1505 
1506  MeQuadBlossomImpl blossom(points, triangles);
1507  blossom.GetEdges(17);
1508 
1509  VecEdge expect_interior = {};
1510  VecEdge expect_boundary = {
1511  {9, 6, 0, XM_NONE, 2, XM_NONE},
1512  {6, 2, 0, XM_NONE, 9, XM_NONE},
1513  {2, 9, 0, XM_NONE, 6, XM_NONE},
1514  };
1515  VecMeEdge expect_extra = {};
1516  VecInt2d expectExtraVertices = {};
1517  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
1518  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
1519  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
1520  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
1521  }
1522 
1523  {
1524  // two adjacent
1525  VecInt2d triangles = {{2, 9, 6}, {9, 4, 6}};
1526  VecPt3d points(10, Pt3d());
1527 
1528  MeQuadBlossomImpl blossom(points, triangles);
1529  blossom.GetEdges(17);
1530 
1531  VecEdge expect_interior = {{6, 9, 1, 0, 4, 2}};
1532  VecEdge expect_boundary = {{9, 4, 1, XM_NONE, 6, XM_NONE},
1533  {6, 2, 0, XM_NONE, 9, XM_NONE},
1534  {4, 6, 1, XM_NONE, 9, XM_NONE},
1535  {2, 9, 0, XM_NONE, 6, XM_NONE}};
1536  VecMeEdge expect_extra = {};
1537  VecInt2d expectExtraVertices = {};
1538  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
1539  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
1540  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
1541  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
1542  }
1543 
1544  {
1545  // three adjacent
1546  VecInt2d triangles = {{2, 9, 6}, {9, 4, 6}, {9, 3, 4}};
1547  VecPt3d points(10, Pt3d());
1548 
1549  MeQuadBlossomImpl blossom(points, triangles);
1550  blossom.GetEdges(17);
1551 
1552  VecEdge expect_interior = {
1553  {6, 9, 1, 0, 4, 2},
1554  {4, 9, 2, 1, 3, 6},
1555  };
1556  VecEdge expect_boundary = {
1557  {9, 3, 2, XM_NONE, 4, XM_NONE}, {6, 2, 0, XM_NONE, 9, XM_NONE},
1558  {4, 6, 1, XM_NONE, 9, XM_NONE}, {3, 4, 2, XM_NONE, 9, XM_NONE},
1559  {2, 9, 0, XM_NONE, 6, XM_NONE},
1560  };
1561  VecMeEdge expect_extra = {{0, 2, 17}};
1562  VecInt2d expectExtraVertices = {{9, 6, 2, 3, 4}};
1563  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
1564  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
1565  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
1566  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
1567  }
1568 
1569  {
1570  // 3x3 grid
1571  VecInt2d triangles = {{4, 3, 0}, {1, 4, 0}, {2, 4, 1}, {5, 4, 2},
1572  {7, 6, 3}, {8, 7, 4}, {5, 8, 4}, {4, 7, 3}};
1573  VecPt3d points(9, Pt3d());
1574 
1575  MeQuadBlossomImpl blossom(points, triangles);
1576  blossom.GetEdges(17);
1577  VecEdge expect_interior = {
1578  {4, 7, 7, 5, 3, 8}, // 0
1579  {4, 8, 5, 6, 7, 5}, // 1
1580  {4, 5, 6, 3, 8, 2}, // 2
1581  {3, 7, 4, 7, 6, 4}, // 3
1582  {3, 4, 7, 0, 7, 0}, // 4
1583  {2, 4, 2, 3, 1, 5}, // 5
1584  {1, 4, 1, 2, 0, 2}, // 6
1585  {0, 4, 0, 1, 3, 1}, // 7
1586  };
1587  VecEdge expect_boundary = {
1588  {8, 7, 5, XM_NONE, 4, XM_NONE}, // 0
1589  {7, 6, 4, XM_NONE, 3, XM_NONE}, // 1
1590  {6, 3, 4, XM_NONE, 7, XM_NONE}, // 2
1591  {5, 8, 6, XM_NONE, 4, XM_NONE}, // 3
1592  {3, 0, 0, XM_NONE, 4, XM_NONE}, // 4
1593  {2, 5, 3, XM_NONE, 4, XM_NONE}, // 5
1594  {1, 2, 2, XM_NONE, 4, XM_NONE}, // 6
1595  {0, 1, 1, XM_NONE, 4, XM_NONE}, // 7
1596  };
1597  VecMeEdge expect_extra = {{4, 0, 17}, {5, 4, 17}};
1598  VecInt2d expectExtraVertices = {{3, 7, 6, 0, 4}, {7, 4, 8, 6, 3}};
1599  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
1600  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
1601  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
1602  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
1603  }
1604 
1605  {
1606  // 3x3 grid with a hole where face { 3, 4, 7} was in the earlier test
1607  // and with { 3, 7, 6 } split into { 3, 7, 9 } and { 3, 9, 6}
1608  VecInt2d triangles = {{4, 3, 0}, {1, 4, 0}, {2, 4, 1}, {5, 4, 2},
1609  {3, 7, 9}, {8, 7, 4}, {5, 8, 4}, {3, 9, 6}};
1610  VecPt3d points(10, Pt3d());
1611 
1612  MeQuadBlossomImpl blossom(points, triangles);
1613  blossom.GetEdges(17);
1614  VecEdge expect_interior = {
1615  {4, 8, 5, 6, 7, 5}, // 0
1616  {4, 5, 6, 3, 8, 2}, // 1
1617  {3, 9, 7, 4, 6, 7}, // 2
1618  {2, 4, 2, 3, 1, 5}, // 3
1619  {1, 4, 1, 2, 0, 2}, // 4
1620  {0, 4, 0, 1, 3, 1}, // 5
1621  };
1622  VecEdge expect_boundary = {
1623  {9, 6, 7, XM_NONE, 3, XM_NONE}, // 0
1624  {8, 7, 5, XM_NONE, 4, XM_NONE}, // 1
1625  {7, 9, 4, XM_NONE, 3, XM_NONE}, // 2
1626  {7, 4, 5, XM_NONE, 8, XM_NONE}, // 3
1627  {6, 3, 7, XM_NONE, 9, XM_NONE}, // 4
1628  {5, 8, 6, XM_NONE, 4, XM_NONE}, // 5
1629  {4, 3, 0, XM_NONE, 0, XM_NONE}, // 6
1630  {3, 0, 0, XM_NONE, 4, XM_NONE}, // 7
1631  {3, 7, 4, XM_NONE, 9, XM_NONE}, // 8
1632  {2, 5, 3, XM_NONE, 4, XM_NONE}, // 9
1633  {1, 2, 2, XM_NONE, 4, XM_NONE}, // 10
1634  {0, 1, 1, XM_NONE, 4, XM_NONE}, // 11
1635  };
1636  VecMeEdge expect_extra = {{5, 0, 17}};
1637  VecInt2d expectExtraVertices = {{4, 8, 7, 3, 0}};
1638  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
1639  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
1640  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
1641  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
1642  }
1643 } // MeQuadBlossomUnitTests::testGetEdges
1644 //------------------------------------------------------------------------------
1646 //------------------------------------------------------------------------------
1648 {
1649  {
1650  VecInt2d triangles = {{4, 3, 0}, {1, 4, 0}, {2, 4, 1}, {5, 4, 2},
1651  {7, 6, 3}, {8, 7, 4}, {5, 8, 4}, {4, 7, 3}};
1652  VecPt3d points(9, Pt3d());
1653 
1654  MeQuadBlossomImpl blossom(points, triangles);
1655  blossom.GetEdges(17);
1656  VecInt2d quads = blossom.EliminateEdges({1, 3, 5, 7});
1657  VecInt2d expect = {{0, 1, 4, 3}, {2, 5, 4, 1}, {3, 4, 7, 6}, {4, 5, 8, 7}};
1658  TS_ASSERT_EQUALS(expect, quads);
1659  }
1660 
1661  {
1662  VecInt2d triangles = {{4, 3, 0}, {1, 4, 0}, {2, 4, 1}, {5, 4, 2},
1663  {3, 7, 9}, {8, 7, 4}, {5, 8, 4}, {3, 9, 6}};
1664  VecPt3d points(10, Pt3d());
1665  MeQuadBlossomImpl blossom(points, triangles);
1666  blossom.GetEdges(17);
1667  VecInt2d quads = blossom.EliminateEdges({2, 5, 3, 0});
1668 
1669  VecInt2d expect = {{0, 1, 4, 3}, {2, 5, 4, 1}, {4, 5, 8, 7}, {3, 7, 9, 6}};
1670  TS_ASSERT_EQUALS(expect, quads);
1671  }
1672 } // MeQuadBlossomUnitTests::testEliminateEdges
1673 //------------------------------------------------------------------------------
1675 //------------------------------------------------------------------------------
1677 {
1678  // clang-format off
1679  VecPt3d points = {{0, 0, 0}, {10, 0, 0}, {20, 0, 0}, {30, 0, 0},
1680  {0, 10, 0}, {10, 10, 0}, {20, 10, 0},
1681  {0, 20, 0}, {10, 20, 0},
1682  {0, 30, 0}};
1683  // clang-format on
1684  VecInt2d triangles = {{0, 1, 4}, {1, 5, 4}, {1, 2, 5}, {2, 6, 5}, {2, 3, 6},
1685  {4, 5, 7}, {5, 8, 7}, {5, 6, 8}, {7, 8, 9}};
1686  // VecInt2d edges = {{0, 1, 15}, {1, 2, 10}, {2, 3, 15}, {3, 4, 10},
1687  // {1, 5, 10}, {3, 7, 10}, {5, 6, 15}, {6, 7, 10},
1688  // {6, 8, 10},
1689  // {0, 2, -10}, {2, 4, -10}, {4, 7, -10}, {7, 8, -10},
1690  // {8, 5, -10}, {5, 0, -10}};
1691  {
1692  MeQuadBlossomImpl blossom(points, triangles);
1693  int cost = -10;
1694  bool splitBoundaryPoints = false;
1695  bool useAngleCost = true;
1696  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1697  const VecInt& cells = ugrid->GetCellstream();
1698  VecInt expectedCells = {
1699  XMU_QUAD, 4, 1, 5, 4, 0, // 0
1700  XMU_QUAD, 4, 2, 6, 5, 1, // 1
1701  XMU_TRIANGLE, 3, 2, 3, 6, // 2
1702  XMU_QUAD, 4, 5, 8, 7, 4, // 3
1703  XMU_TRIANGLE, 3, 5, 6, 8, // 4
1704  XMU_TRIANGLE, 3, 7, 8, 9 // 5
1705  };
1706  TS_ASSERT_EQUALS(expectedCells, cells);
1707  const VecPt3d& actualPoints = ugrid->GetLocations();
1708  TS_ASSERT_DELTA_VECPT3D(points, actualPoints, 1.0e-5);
1709  }
1710 
1711  {
1712  MeQuadBlossomImpl blossom(points, triangles);
1713  int cost = -10;
1714  bool splitBoundaryPoints = true;
1715  bool useAngleCost = true;
1716  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1717  const VecInt& cells = ugrid->GetCellstream();
1718  VecInt expectedCells = {
1719  XMU_QUAD, 4, 1, 5, 4, 0, // 0
1720  XMU_QUAD, 4, 2, 10, 5, 1, // 1
1721  XMU_QUAD, 4, 2, 3, 6, 10, // 2
1722  XMU_QUAD, 4, 5, 8, 7, 4, // 3
1723  XMU_QUAD, 4, 6, 8, 5, 10, // 4
1724  XMU_TRIANGLE, 3, 7, 8, 9 // 5
1725  };
1726  TS_ASSERT_EQUALS(expectedCells, cells);
1727  const VecPt3d& actualPoints = ugrid->GetLocations();
1728  VecPt3d expectedPoints = points;
1729  expectedPoints.push_back(Pt3d(50.0/3.0, 20.0/3.0, 0.0));
1730  TS_ASSERT_DELTA_VECPT3D(expectedPoints, actualPoints, 1.0e-5);
1731  }
1732 
1733  {
1734  // clang-format off
1735  VecPt3d points = { {-10, 0, 0}, {0, 0, 0}, {10, 0, 0}, {-5, 7.07, 0}, {5, 7.07, 0}, {0, 14.14, 0} };
1736  // clang-format on
1737  VecInt2d triangles = { {0, 1, 3}, {1, 2, 4}, {1, 4, 3}, {3, 4, 5} };
1738 
1739  MeQuadBlossomImpl blossom(points, triangles);
1740  int cost = -10;
1741  bool splitBoundaryPoints = true;
1742  bool useAngleCost = false;
1743  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1744  const VecInt& cells = ugrid->GetCellstream();
1745  VecInt expectedCells = {
1746  XMU_QUAD, 4, 3, 0, 1, 6, // 0
1747  XMU_QUAD, 4, 1, 2, 4, 6, // 1
1748  XMU_QUAD, 4, 3, 6, 4, 5 // 2
1749  };
1750  TS_ASSERT_EQUALS(expectedCells, cells);
1751  const VecPt3d& actualPoints = ugrid->GetLocations();
1752  VecPt3d expectedPoints = points;
1753  expectedPoints.push_back(Pt3d(0, 14.14 / 3, 0));
1754  TS_ASSERT_DELTA_VECPT3D(expectedPoints, actualPoints, 1.0e-5);
1755  }
1756 } // MeQuadBlossomUnitTests::testSimpleTriangle
1757 //------------------------------------------------------------------------------
1759 //------------------------------------------------------------------------------
1761 {
1762  // clang-format off
1763  VecPt3d points = {
1764  {0, 0, 0}, {10, 0, 0}, {20, 0, 0}, {30, 0, 0},
1765  {0, 10, 0}, {10, 10, 0}, {20, 10, 0}, {30, 10, 0},
1766  {0, 20, 0}, {10, 20, 0}, {20, 20, 0}, {30, 20, 0},
1767  {0, 30, 0}, {10, 30, 0}, {20, 30, 0}, {30, 30, 0}};
1768  VecInt2d triangles = {
1769  {0, 1, 4}, {1, 5, 4}, {1, 2, 5}, {2, 6, 5}, {2, 3, 6}, {3, 7, 6},
1770  {4, 9, 8}, {4, 5, 9}, {5, 10, 9}, {5, 6, 10}, {6, 7, 11}, {6, 11, 10},
1771  {8, 9, 12}, {9, 13, 12}, {9, 10, 13}, {10, 14, 13}, {10, 11, 14},
1772  {11, 15, 14}};
1773 
1774  {
1775  MeQuadBlossomImpl blossom(points, triangles);
1776  int cost = -10;
1777  bool splitBoundaryPoints = false;
1778  bool useAngleCost = true;
1779  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1780  const VecInt& cells = ugrid->GetCellstream();
1781  VecInt expectedCells = {
1782  XMU_QUAD, 4, 1, 5, 4, 0, // 0
1783  XMU_QUAD, 4, 2, 6, 5, 1, // 1
1784  XMU_QUAD, 4, 3, 7, 6, 2, // 2
1785  XMU_QUAD, 4, 4, 5, 9, 8, // 3
1786  XMU_QUAD, 4, 5, 6, 10, 9, // 4
1787  XMU_QUAD, 4, 6, 7, 11, 10, // 5
1788  XMU_QUAD, 4, 9, 13, 12, 8, // 6
1789  XMU_QUAD, 4, 10, 14, 13, 9, // 7
1790  XMU_QUAD, 4, 11, 15, 14, 10, // 8
1791  };
1792  TS_ASSERT_EQUALS(expectedCells, cells);
1793  const VecPt3d& actualPoints = ugrid->GetLocations();
1794  VecPt3d expectedPoints = points;
1795  TS_ASSERT_DELTA_VECPT3D(expectedPoints, actualPoints, 1.0e-5);
1796  }
1797 
1798  {
1799  MeQuadBlossomImpl blossom(points, triangles);
1800  int cost = -10;
1801  bool splitBoundaryPoints = true;
1802  bool useAngleCost = true;
1803  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1804  const VecInt& cells = ugrid->GetCellstream();
1805  VecInt expectedCells = {
1806  XMU_QUAD, 4, 1, 5, 4, 0, // 0
1807  XMU_QUAD, 4, 2, 6, 5, 1, // 1
1808  XMU_QUAD, 4, 3, 7, 6, 2, // 2
1809  XMU_QUAD, 4, 4, 5, 9, 8, // 3
1810  XMU_QUAD, 4, 5, 6, 10, 9, // 4
1811  XMU_QUAD, 4, 6, 7, 11, 10, // 5
1812  XMU_QUAD, 4, 9, 13, 12, 8, // 6
1813  XMU_QUAD, 4, 10, 14, 13, 9, // 7
1814  XMU_QUAD, 4, 11, 15, 14, 10, // 8
1815  };
1816  TS_ASSERT_EQUALS(expectedCells, cells);
1817  const VecPt3d& actualPoints = ugrid->GetLocations();
1818  VecPt3d expectedPoints = points;
1819  TS_ASSERT_DELTA_VECPT3D(expectedPoints, actualPoints, 1.0e-5);
1820  }
1821 } // MeQuadBlossomUnitTests::testSimpleQuad
1822 //------------------------------------------------------------------------------
1824 // two on the horizontal bottom row increasing to six divisions on top row.
1825 //------------------------------------------------------------------------------
1827 {
1828  // clang-format off
1829  VecPt3d points = {
1830  {-10, 0, 0}, {0, 0, 0}, {10, 0,0},
1831  {-15, 10, 0}, {-5, 10, 0}, {5, 10, 0}, {15, 10, 0},
1832  {-20, 20, 0}, {-10, 20, 0}, {0, 20, 0}, {10, 20, 0}, {20, 20, 0},
1833  {-25, 30, 0}, {-15, 30, 0}, {-5, 30, 0}, {5, 30, 0}, {15, 30, 0}, {25, 30, 0},
1834  {-30, 40, 0}, {-20, 40, 0}, {-10, 40, 0}, {0, 40, 0}, {10, 40, 0}, {20, 40, 0}, {30, 40, 0}};
1835  VecInt2d triangles = {
1836  {0, 4, 3}, {0, 1, 4}, {1, 5, 4}, {1, 2, 5}, {2, 6, 5},
1837  {3, 8, 7}, {3, 4, 8}, {4, 9, 8}, {4, 5, 9}, {5, 10, 9}, {5, 11, 10}, {5, 6, 11},
1838  {7, 13, 12}, {7, 8, 13}, {8, 14, 13}, {8, 9, 14}, {9, 15, 14}, {9, 10, 15}, {10, 16, 15}, {10, 11, 16}, {11, 17, 16},
1839  {12, 19, 18}, {12, 13, 19}, {13, 20, 19}, {13, 14, 20}, {14, 21, 20}, {14, 15, 21}, {15, 22, 21}, {15, 23, 22}, {15, 16, 23}, {16, 17, 23}, {17, 24, 23}};
1840 
1841  {
1842  MeQuadBlossomImpl blossom(points, triangles);
1843  int cost = -10;
1844  bool splitBoundaryPoints = false;
1845  bool useAngleCost = true;
1846  int numBoundaryEdges = blossom.PreMakeQuads();
1847  TS_ASSERT_EQUALS(16, numBoundaryEdges);
1848  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1849  const VecInt& cells = ugrid->GetCellstream();
1850  VecInt expectedCells = {
1851  XMU_TRIANGLE, 3, 0, 4, 3, // 0
1852  XMU_QUAD, 4, 1, 5, 4, 0, // 1
1853  XMU_QUAD, 4, 2, 6, 5, 1, // 2
1854  XMU_TRIANGLE, 3, 3, 8, 7, // 3
1855  XMU_QUAD, 4, 4, 9, 8, 3, // 4
1856  XMU_QUAD, 4, 5, 10, 9, 4, // 5
1857  XMU_QUAD, 4, 5, 6, 11, 10, // 6
1858  XMU_TRIANGLE, 3, 7, 13, 12, // 7
1859  XMU_QUAD, 4, 8, 14, 13, 7, // 8
1860  XMU_QUAD, 4, 9, 15, 14, 8, // 9
1861  XMU_QUAD, 4, 10, 16, 15, 9, // 10
1862  XMU_QUAD, 4, 11, 17, 16, 10, // 11
1863  XMU_TRIANGLE, 3, 12, 19, 18, // 12
1864  XMU_QUAD, 4, 13, 20, 19, 12, // 13
1865  XMU_QUAD, 4, 14, 21, 20, 13, // 14
1866  XMU_QUAD, 4, 15, 22, 21, 14, // 15
1867  XMU_QUAD, 4, 15, 16, 23, 22, // 16
1868  XMU_QUAD, 4, 17, 24, 23, 16 // 17
1869  };
1870  TS_ASSERT_EQUALS(expectedCells, cells);
1871  const VecPt3d& actualPoints = ugrid->GetLocations();
1872  VecPt3d expectedPoints = points;
1873  TS_ASSERT_DELTA_VECPT3D(expectedPoints, actualPoints, 1.0e-5);
1874  }
1875 
1876  {
1877  MeQuadBlossomImpl blossom(points, triangles);
1878  int cost = -10;
1879  bool splitBoundaryPoints = true;
1880  bool useAngleCost = true;
1881  BSHP<XmUGrid> ugrid = blossom._MakeQuads(splitBoundaryPoints, useAngleCost, cost);
1882  const VecInt& cells = ugrid->GetCellstream();
1883  VecInt expectedCells = {
1884  XMU_QUAD, 4, 3, 0, 4, 25, // 0
1885  XMU_QUAD, 4, 1, 5, 4, 0, // 1
1886  XMU_QUAD, 4, 2, 6, 5, 1, // 2
1887  XMU_QUAD, 4, 8, 7, 3, 25, // 3
1888  XMU_QUAD, 4, 4, 9, 8, 25, // 4
1889  XMU_QUAD, 4, 5, 10, 9, 4, // 5
1890  XMU_QUAD, 4, 5, 6, 11, 10, // 6
1891  XMU_QUAD, 4, 12, 7, 13, 26, // 7
1892  XMU_QUAD, 4, 8, 14, 13, 7, // 8
1893  XMU_QUAD, 4, 9, 15, 14, 8, // 9
1894  XMU_QUAD, 4, 10, 16, 15, 9, // 10
1895  XMU_QUAD, 4, 11, 17, 16, 10, // 11
1896  XMU_QUAD, 4, 19, 18, 12, 26, // 12
1897  XMU_QUAD, 4, 13, 20, 19, 26, // 13
1898  XMU_QUAD, 4, 14, 21, 20, 13, // 14
1899  XMU_QUAD, 4, 15, 22, 21, 14, // 15
1900  XMU_QUAD, 4, 15, 16, 23, 22, // 16
1901  XMU_QUAD, 4, 17, 24, 23, 16, // 17
1902  };
1903  TS_ASSERT_EQUALS(expectedCells, cells);
1904  const VecPt3d& actualPoints = ugrid->GetLocations();
1905  VecPt3d expectedPoints = points;
1906  expectedPoints.push_back({-10, 13.3333, 0.0});
1907  expectedPoints.push_back({-20.0, 33.3333, 0.0});
1908  TS_ASSERT_DELTA_VECPT3D(expectedPoints, actualPoints, 1.0e-3);
1909  }
1910 } // MeQuadBlossomUnitTests::testComplexQuad
1911 //------------------------------------------------------------------------------
1913 //------------------------------------------------------------------------------
1915 {
1916  // clang-format off
1918  VecPt3d points = { { 0, 0, 0 }, { 10, 0, 0 }, { 20, 0, 0 }, { 30, 0, 0 },
1919  {0, 10, 0}, {10, 10, 0}, {20, 10, 0}, {30, 10, 0},
1920  {0, 20, 0}, {10, 20, 0}, {20, 20, 0}, {30, 20, 0}
1921  };
1922 
1923  // Cell type (5), number of points (3), point numbers, counterclockwise
1924  VecInt cells = {
1925  // row 1
1926  XMU_TRIANGLE, 3, 0, 5, 4,
1927  XMU_TRIANGLE, 3, 0, 1, 5,
1928  XMU_TRIANGLE, 3, 1, 2, 5,
1929  XMU_TRIANGLE, 3, 2, 6, 5,
1930  XMU_QUAD, 4, 2, 3, 7, 6,
1931  // row 2
1932  XMU_TRIANGLE, 3, 4, 5, 8,
1933  XMU_POLYGON, 3, 5, 9, 8,
1934  XMU_QUAD, 4, 5, 6, 10, 9,
1935  XMU_POLYGON, 4, 6, 7, 11, 10
1936  };
1938  // clang-format on
1939 
1940  BSHP<XmUGrid> ugrid = XmUGrid::New(points, cells);
1941 
1942  BSHP<XmUGrid> quadUGrid = MeQuadBlossom::SplitToQuads(ugrid);
1943 
1944  VecPt3d quadPoints = quadUGrid->GetLocations();
1945  // clang-format off
1946  VecPt3d expectedQuadPoints = {
1947  { 0, 0, 0 }, { 10, 0, 0 }, { 20, 0, 0 }, { 30, 0, 0 },
1948  {0, 10, 0}, {10, 10, 0}, {20, 10, 0}, {30, 10, 0},
1949  {0, 20, 0}, {10, 20, 0}, {20, 20, 0}, {30, 20, 0},
1950  // The split edges
1951  {5, 0, 0}, {0, 5, 0}, {5, 5, 0}, {15, 0, 0}, {10, 5, 0}, // 12-16
1952  {25, 0, 0}, {15, 5, 0}, {20, 5, 0}, {30, 5, 0}, {5, 10, 0}, // 17-21
1953  {0, 15, 0}, {15, 10, 0}, {5, 15, 0}, {10, 15, 0}, {25, 10, 0}, // 22-26
1954  {20, 15, 0}, {30, 15, 0}, {5, 20, 0}, {15, 20, 0}, {25, 20, 0}, // 27-31
1955  // The centroids
1956  {3.33333, 6.66667, 0}, {6.66667, 3.33333, 0}, // 32-33
1957  {13.33333, 3.33333, 0}, {16.66667, 6.66667, 0}, // 34-35
1958  {25, 5, 0}, {3.33333, 13.33333, 0}, {6.66667, 16.66667, 0}, // 36-38
1959  {15, 15, 0}, {25, 15, 0} //39-40
1960  };
1961  TS_ASSERT_DELTA_VECPT3D(expectedQuadPoints, quadPoints, 1.0e-5);
1962 
1963  VecInt expectedQuadCells = {
1964  XMU_QUAD, 4, 0, 14, 32, 13,
1965  XMU_QUAD, 4, 5, 21, 32, 14,
1966  XMU_QUAD, 4, 4, 13, 32, 21,
1967  XMU_QUAD, 4, 0, 12, 33, 14,
1968  XMU_QUAD, 4, 1, 16, 33, 12,
1969  XMU_QUAD, 4, 5, 14, 33, 16,
1970  XMU_QUAD, 4, 1, 15, 34, 16,
1971  XMU_QUAD, 4, 2, 18, 34, 15,
1972  XMU_QUAD, 4, 5, 16, 34, 18,
1973  XMU_QUAD, 4, 2, 19, 35, 18,
1974  XMU_QUAD, 4, 6, 23, 35, 19,
1975  XMU_QUAD, 4, 5, 18, 35, 23,
1976  XMU_QUAD, 4, 2, 17, 36, 19,
1977  XMU_QUAD, 4, 3, 20, 36, 17,
1978  XMU_QUAD, 4, 7, 26, 36, 20,
1979  XMU_QUAD, 4, 6, 19, 36, 26,
1980  XMU_QUAD, 4, 4, 21, 37, 22,
1981  XMU_QUAD, 4, 5, 24, 37, 21,
1982  XMU_QUAD, 4, 8, 22, 37, 24,
1983  XMU_QUAD, 4, 5, 25, 38, 24,
1984  XMU_QUAD, 4, 9, 29, 38, 25,
1985  XMU_QUAD, 4, 8, 24, 38, 29,
1986  XMU_QUAD, 4, 5, 23, 39, 25,
1987  XMU_QUAD, 4, 6, 27, 39, 23,
1988  XMU_QUAD, 4, 10, 30, 39, 27,
1989  XMU_QUAD, 4, 9, 25, 39, 30,
1990  XMU_QUAD, 4, 6, 26, 40, 27,
1991  XMU_QUAD, 4, 7, 28, 40, 26,
1992  XMU_QUAD, 4, 11, 31, 40, 28,
1993  XMU_QUAD, 4, 10, 27, 40, 31
1994  };
1995  // clang-format on
1996  VecInt quadCells = quadUGrid->GetCellstream();
1997  TS_ASSERT_EQUALS_VEC(expectedQuadCells, quadCells);
1998 } // MeQuadBlossomUnitTests::testSplitToQuads
1999 //------------------------------------------------------------------------------
2001 //------------------------------------------------------------------------------
2003 {
2004  TS_ASSERT_DELTA(0.0085, MeQuadBlossom::EstimatedRunTimeInMinutes(500), 0.001);
2005  TS_ASSERT_DELTA(0.0680, MeQuadBlossom::EstimatedRunTimeInMinutes(1000), 0.001);
2006  TS_ASSERT_DELTA(8.5000, MeQuadBlossom::EstimatedRunTimeInMinutes(5000), 0.001);
2007  TS_ASSERT_DELTA(68.0, MeQuadBlossom::EstimatedRunTimeInMinutes(10000), 0.001);
2008  TS_ASSERT_DELTA(68000.0, MeQuadBlossom::EstimatedRunTimeInMinutes(100000), 0.001);
2009 } // MeQuadBlossomUnitTests::testEstimatedRunTime
2010 //------------------------------------------------------------------------------
2012 //------------------------------------------------------------------------------
2014 {
2015  {
2016  // single triangle
2017  // 2-----1
2018  // | /
2019  // | /
2020  // | /
2021  // | /
2022  // 0
2023  VecInt2d triangles = {{0, 1, 2}};
2024  VecPt3d points = {{0, 0, 0}, {10, 10, 0}, {0, 10, 0}};
2025 
2026  MeQuadBlossomImpl blossom(points, triangles);
2027  int numBoundaryEdges = blossom.PreMakeQuads();
2028  TS_ASSERT_EQUALS(3, numBoundaryEdges);
2029 
2030  VecEdge expect_interior = {};
2031  VecEdge expect_boundary = {
2032  {2, 0, 0, XM_NONE, 1, XM_NONE},
2033  {1, 2, 0, XM_NONE, 0, XM_NONE},
2034  {0, 1, 0, XM_NONE, 2, XM_NONE},
2035  };
2036  VecMeEdge expect_extra = {};
2037  VecInt2d expectExtraVertices = {};
2038  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
2039  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
2040  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
2041  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
2042 
2043  bool splitBoundaryPoints = true;
2044  bool useAngle = false;
2045  blossom.MakeQuads(splitBoundaryPoints, useAngle);
2046 
2047  VecInt2d faces = blossom.m_faces;
2048  TS_ASSERT_EQUALS(triangles, faces);
2049  }
2050 
2051  {
2052  // two adjacent
2053  // 2-----3
2054  // | /|
2055  // | / |
2056  // | / |
2057  // | / |
2058  // 0-----1
2059  VecInt2d triangles = {{2, 0, 3}, {3, 0, 1}};
2060  VecPt3d points = {{0, 0, 0}, {10, 0, 0}, {0, 10, 0}, {10, 10, 0}};
2061 
2062  MeQuadBlossomImpl blossom(points, triangles);
2063  int numBoundaryEdges = blossom.PreMakeQuads();
2064  TS_ASSERT_EQUALS(4, numBoundaryEdges);
2065 
2066  VecEdge expect_interior = {{0, 3, 0, 1, 2, 1}};
2067  VecEdge expect_boundary = {{3, 2, 0, XM_NONE, 0, XM_NONE},
2068  {2, 0, 0, XM_NONE, 3, XM_NONE},
2069  {1, 3, 1, XM_NONE, 0, XM_NONE},
2070  {0, 1, 1, XM_NONE, 3, XM_NONE}
2071  };
2072  VecMeEdge expect_extra = {};
2073  VecInt2d expectExtraVertices = {};
2074  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
2075  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
2076  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
2077  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
2078 
2079  bool splitBoundaryPoints = true;
2080  bool useAngle = false;
2081  blossom.MakeQuads(splitBoundaryPoints, useAngle);
2082 
2083  VecInt2d faces = blossom.m_faces;
2084  VecInt2d expectedFaces = {{0, 1, 3, 2}};
2085  TS_ASSERT_EQUALS(expectedFaces, faces);
2086  }
2087 
2088  {
2089  // three adjacent
2090  VecInt2d triangles = {{2, 9, 6}, {9, 4, 6}, {9, 3, 4}};
2091  VecPt3d points(10, Pt3d());
2092  points[2] = {0, 0, 0};
2093  points[3] = {10, 20, 0};
2094  points[4] = {0, 20, 0};
2095  points[6] = {0, 10, 0};
2096  points[9] = {10, 10, 0};
2097 
2098 
2099  MeQuadBlossomImpl blossom(points, triangles);
2100  int numBoundaryEdges = blossom.PreMakeQuads();
2101  TS_ASSERT_EQUALS(5, numBoundaryEdges);
2102 
2103  VecEdge expect_interior = {
2104  {6, 9, 1, 0, 4, 2},
2105  {4, 9, 2, 1, 3, 6},
2106  };
2107  VecEdge expect_boundary = {
2108  {9, 3, 2, XM_NONE, 4, XM_NONE}, {6, 2, 0, XM_NONE, 9, XM_NONE},
2109  {4, 6, 1, XM_NONE, 9, XM_NONE}, {3, 4, 2, XM_NONE, 9, XM_NONE},
2110  {2, 9, 0, XM_NONE, 6, XM_NONE},
2111  };
2112  VecMeEdge expect_extra = {{0, 2, -1000}};
2113  VecInt2d expectExtraVertices = {{9, 6, 2, 3, 4}};
2114  TS_ASSERT_EQUALS(expect_interior, blossom.m_interiorEdges);
2115  TS_ASSERT_EQUALS(expect_boundary, blossom.m_boundaryEdges);
2116  TS_ASSERT_EQUALS(expect_extra, blossom.m_extraEdges);
2117  TS_ASSERT_EQUALS(expectExtraVertices, blossom.m_extraPoints);
2118 
2119  bool splitBoundaryPoints = true;
2120  bool useAngle = false;
2121  blossom.MakeQuads(splitBoundaryPoints, useAngle);
2122 
2123  VecInt2d faces = blossom.m_faces;
2124  VecInt2d expectedFaces = {{2, 9, 6}, {4, 6, 9, 3}};
2125  TS_ASSERT_EQUALS(expectedFaces, faces);
2126  }
2127 } // MeQuadBlossomUnitTests::testPreMakeQuads
2128 
2129 #endif // CXX_TEST
VecEdge m_interiorEdges
The interior edges of the triangular mesh.
void testGetInteriorEdges()
Test GetInteriorEdges function.
std::vector< int > VecInt
void testExtractCellsAdjacentToPoint()
Test ExtractCellsAdjacentToPoint function.
int m_fL
Face on the left looking from m_p0 to m_p1.
void testProcessVertexChains()
Test ProcessVertexChains function.
void testEliminateEdges()
Test GetAngle function.
void testSimpleQuad()
Test simple quad with three divisions on each side.
VecInt2d m_faces
initially triangles becomes quads
#define XM_NONE
void testEta()
Test GetEtaAngle and GetEtaDistance functions.
#define TS_ASSERT_DELTA_VECPT3D(a, b, delta)
const double TWO_PI
2 times PI
double gmXyDistanceSquared(const Pt3d &pt1, const Pt3d &pt2)
int GetSecond() const
static double EstimatedRunTimeInMinutes(int a_numPoints)
Get the estimated time to run the Quad Blossom algorithm in minutes.
virtual ~MeQuadBlossom()
Destructor.
static BSHP< XmUGrid > New()
Class to convert 2D grid of triangles to quads.
Definition: MeQuadBlossom.h:34
void testSplitToQuads()
Test MeQuadBlossom::SplitToQuads.
VecInt2d m_extraPoints
VecMeEdge m_extraEdges
The adjacent boundary triangles that can be split. { face0, face1, cost }.
void testEstimatedRunTime()
Test function to get estimated time to run Quad Blossom algorithm.
int m_pR
Point on the face (triangle) to the right opposite the edge.
int m_fR
Face on the right looking from m_p0 to m_p1.
std::vector< VecInt > VecInt2d
const double PI_OVER_TWO
PI divided by 2.
void testGetEdges()
Test GetAngle function.
MeQuadBlossom()
Constructor.
void testComplexQuad()
Test complex quad with three divisions on two opposite sides and.
Pt3d gmComputeCentroid(const VecPt3d &a_points)
int m_p1
Point 1.
std::vector< edgerecord > VecEdge
#define XM_ASSERT(x)
int m_nextPoint
Index of the point going out from point p.
Pt3d gmCreateVector(const Pt3d &a_p1, const Pt3d &a_p2)
#define XM_PI
VecMeEdge m_costs
const int DEFAULT_COST
The default weight for extra edges.
void testGetAngle()
Test GetAngle function.
int m_pL
Point on the face (triangle) to the left opposite the edge.
int m_p0
Point 0.
int m_cell
Cell index.
static BSHP< XmUGrid > SplitToQuads(BSHP< XmUGrid > a_ugrid)
Splits UGrid with 2D cells into quads by adding midpoints for each edge and creating a quad by attach...
VecPt3d m_splitPoints
The potential new points for split points.
#define TS_ASSERT_EQUALS_VEC(a, b)
const double TWO_OVER_PI
2 divided by PI
VecPt3d m_points
points of the triangular mesh
std::vector< MeEdge > VecMeEdge
Vector of MeEdge.
void testSimpleTriangle()
Test simple triangle with three divisions on each side.
int m_priorPoint
Index of the point coming into point p.
int GetFirst() const
std::vector< Pt3d > VecPt3d
VecEdge m_boundaryEdges
The boundary edges of the triangular mesh.
void testGetBoundaryEdges()
Test GetBoundaryEdges function.
void testPreMakeQuads()
Test PreMakeQuads function.