xmsmesh  1.0
MeMultiPolyMesher.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 <fstream>
17 #include <numeric>
18 #include <set>
19 #include <sstream>
20 
21 // 4. External library headers
22 #include <boost/format.hpp>
23 #include <boost/unordered_map.hpp>
25 #include <xmscore/misc/Progress.h>
26 #include <xmscore/misc/XmError.h>
27 #include <xmscore/stl/set.h>
28 #include <xmscore/stl/vector.h>
33 
34 // 5. Shared code headers
37 
38 // 6. Non-shared code headers
39 
40 //----- Forward declarations ---------------------------------------------------
41 
42 //----- External globals -------------------------------------------------------
43 
44 //----- Namespace declaration --------------------------------------------------
45 
46 namespace xms
47 {
48 //----- Constants / Enumerations -----------------------------------------------
49 
50 //----- Classes / Structs ------------------------------------------------------
52 {
53 public:
56 
57  virtual bool MeshIt(MeMultiPolyMesherIo& a_io) override;
58  virtual void CheckForIntersections(const MeMultiPolyMesherIo& a_io,
59  std::string& a_errors) const override;
60 
61 private:
62  void AppendMesh(VecPt3d& a_points, const VecInt& a_triangles, VecInt& a_cells);
63  void AddUniquePoints(const VecPt3d& a_points, VecInt& a_oldDups, VecInt& a_newDups);
64  void RenumberNewMesh(int a_oldNumPts,
65  size_t a_numNewPts,
66  const VecInt& a_oldDups,
67  const VecInt& a_newDups,
68  VecInt& a_cells) const;
69  void AppendNewCells(const VecInt& a_cells);
70  void ReportUnusedRefinePts(const MeMultiPolyMesherIo& a_io, const VecPt3d& a_usedPts);
72  bool ValidateInput(const MeMultiPolyMesherIo& a_io);
73  bool ExtentsOverlap(const Pt3d& oneMn,
74  const Pt3d& oneMx,
75  const Pt3d& two1,
76  const Pt3d& two2) const;
77 
78  BSHP<VecPt3d> m_pts;
81  boost::unordered_map<std::pair<double, double>, int> m_ptHash;
82 }; // class MeMultiPolyMesherImpl
83 
88 {
89 public:
95  SegmentLocation(int a_poly = 0, int a_polySeg = 0, int a_inPoly = 0, int a_inPolySeg = 0)
96  : m_poly(a_poly)
97  , m_polySeg(a_polySeg)
98  , m_inPoly(a_inPoly)
99  , m_inPolySeg(a_inPolySeg)
100  {
101  }
102 
103  int m_poly;
104  int m_polySeg;
105  int m_inPoly;
107 }; // struct SegmentLocation
108 
109 //----- Internal functions -----------------------------------------------------
110 namespace
111 {
112 //------------------------------------------------------------------------------
116 //------------------------------------------------------------------------------
117 void iWriteInterpDataToDebugFile(std::fstream& a_os, BSHP<InterpBase> a_interp)
118 {
119  BSHP<VecPt3d> ptsPtr = a_interp->GetPts();
120  BSHP<InterpIdw> idw = BDPC<InterpIdw>(a_interp);
121  BSHP<InterpLinear> linear = BDPC<InterpLinear>(a_interp);
122  XM_ENSURE_TRUE(ptsPtr && (idw || linear));
123  if (idw)
124  a_os << "IDW";
125  else
126  a_os << "LINEAR";
127 
128  VecPt3d& pts(*ptsPtr);
129  a_os << pts.size() << "\n";
130  for (const auto& p : pts)
131  a_os << STRstd(p.x) << " " << STRstd(p.y) << "\n";
132 } // iWriteInterpDataToDebugFile
133 //------------------------------------------------------------------------------
136 //------------------------------------------------------------------------------
137 void iWriteInputsToDebugFile(MeMultiPolyMesherIo& a_io)
138 {
139  std::ifstream f("c:\\temp\\xmsmesh_write_inputs.dbg");
140  if (!f.good())
141  return;
142  std::fstream os;
143  os.open("C:\\temp\\xmsmesh_inputs.txt", std::fstream::out);
144  if (os.bad())
145  return;
146  for (size_t i = 0; i < a_io.m_polys.size(); ++i)
147  {
148  MePolyInput& poly(a_io.m_polys[i]);
149  os << "BEGIN_POLYGON\nOUTSIDE " << poly.m_outPoly.size() << "\n";
150  for (auto& p : poly.m_outPoly)
151  os << STRstd(p.x) << " " << STRstd(p.y) << "\n";
152  for (auto& v : poly.m_insidePolys)
153  {
154  os << "INSIDE " << v.size() << "\n";
155  for (auto& p : v)
156  os << STRstd(p.x) << " " << STRstd(p.y) << "\n";
157  }
158  os << "BIAS " << STRstd(poly.m_bias) << "\n";
159  if (poly.m_sizeFunction)
160  {
161  os << "SIZE_FUNCTION\n";
162  iWriteInterpDataToDebugFile(os, poly.m_sizeFunction);
163  }
164  if (poly.m_elevFunction)
165  {
166  os << "ELEVATION_FUNCTION\n";
167  iWriteInterpDataToDebugFile(os, poly.m_elevFunction);
168  }
169  if (poly.m_constSizeFunction != -1)
170  {
171  os << "CONST_SIZE_FUNCTION " << STRstd(poly.m_constSizeFunction) << "\n";
172  }
173  if (!poly.m_polyCorners.empty())
174  {
175  os << "PATCH_CORNERS ";
176  for (size_t j = 0; j < poly.m_polyCorners.size(); ++j)
177  os << poly.m_polyCorners[j] << " ";
178  os << "\n";
179  }
180  if (poly.m_relaxationMethod != "")
181  {
182  os << "RELAXATION_METHOD " << poly.m_relaxationMethod << "\n";
183  }
184  os << "END_POLYGON\n";
185  }
186  if (a_io.m_checkTopology)
187  {
188  os << "CHECK_TOPOLOGY\n";
189  }
190  if (a_io.m_returnCellPolygons)
191  {
192  os << "RETURN_CELL_POLYGONS\n";
193  }
194  if (!a_io.m_refPts.empty())
195  {
196  os << "REFINE_POINTS " << a_io.m_refPts.size() << "\n";
197  for (auto& p : a_io.m_refPts)
198  {
199  os << STRstd(p.m_pt.x) << " " << STRstd(p.m_pt.y) << " " << STRstd(p.m_size) << " "
200  << p.m_createMeshPoint << "\n";
201  }
202  }
203 
204 } // iWriteInputsToDebugFile
205 
206 } // unnamed namespace
207 //----- Class / Function definitions -------------------------------------------
208 
214 //------------------------------------------------------------------------------
216 //------------------------------------------------------------------------------
218 : m_pts(new VecPt3d())
219 , m_cells()
220 , m_cellCount(0)
221 {
222 } // MeMultiPolyMesherImpl::MeMultiPolyMesherImpl
223 //------------------------------------------------------------------------------
229 //------------------------------------------------------------------------------
231 {
232  iWriteInputsToDebugFile(a_io);
234  if (!ValidateInput(a_io))
235  {
236  return false;
237  }
238 
239  // Mesh each polygon and merge the triangles together into one mesh
240  BSHP<MePolyMesher> pm = MePolyMesher::New();
241  std::stringstream ss;
242  ss << "Meshing polygon 1 of " << a_io.m_polys.size();
243  Progress prog(ss.str());
244 
245  VecPt3d pts, refinePts, tmpPts;
246  VecInt tris, cells, cellPolygons;
247  for (size_t i = 0; i < a_io.m_polys.size(); ++i)
248  {
249  pts.resize(0);
250  tris.resize(0);
251  if (pm->MeshIt(a_io, i, pts, tris, cells))
252  {
253  pm->GetProcessedRefinePts(tmpPts);
254  refinePts.insert(refinePts.end(), tmpPts.begin(), tmpPts.end());
255  AppendMesh(pts, tris, cells);
256 
257  // Assign cell polygons
258  if (a_io.m_returnCellPolygons)
259  {
260  cellPolygons.resize(m_cellCount, (int)i);
261  }
262  }
263 
264  // Update progress
265  ss.str("");
266  ss << "Meshing polygon " << i + 2 << " of " << a_io.m_polys.size();
267  prog.UpdateMessage(ss.str());
268  prog.ProgressStatus((double)i / a_io.m_polys.size());
269  }
270 
271  // Move memory and cleanup
272  a_io.m_points.swap(*m_pts);
273  a_io.m_cells.swap(m_cells);
274  a_io.m_cellPolygons.swap(cellPolygons);
275  m_ptHash.clear();
276  m_cellCount = 0;
277 
278  // report unused refine points
279  ReportUnusedRefinePts(a_io, refinePts);
280  return true;
281 } // MeMultiPolyMesherImpl::MeshIt
282 //------------------------------------------------------------------------------
286 //------------------------------------------------------------------------------
288 {
289  double tol(1e-9);
290  // remove repeated first and last points
291  for (size_t i = 0; i < a_io.m_polys.size(); ++i)
292  {
293  MePolyInput& polyInput = a_io.m_polys[i];
294  auto &first = polyInput.m_outPoly.front();
295  auto &last = polyInput.m_outPoly.back();
296  if (gmEqualPointsXY(first, last, tol))
297  {
298  polyInput.m_outPoly.pop_back();
299  }
300 
301  for (size_t j = 0; j < polyInput.m_insidePolys.size(); ++j)
302  {
303  auto &first = polyInput.m_insidePolys[j].front();
304  auto &last = polyInput.m_insidePolys[j].back();
305  if (gmEqualPointsXY(first, last, tol))
306  {
307  polyInput.m_insidePolys[j].pop_back();
308  }
309  }
310  }
311  // check point ordering. We want the outside polygon should be CW and the
312  // inside polygons should be CCW
313  for (size_t i = 0; i < a_io.m_polys.size(); ++i)
314  {
315  MePolyInput& polyInput = a_io.m_polys[i];
316  double area = gmPolygonArea(&polyInput.m_outPoly[0], polyInput.m_outPoly.size());
317  if (area > 0)
318  {
319  std::reverse(polyInput.m_outPoly.begin(), polyInput.m_outPoly.end());
320  }
321 
322  for (size_t j = 0; j < polyInput.m_insidePolys.size(); ++j)
323  {
324  area = gmPolygonArea(&polyInput.m_insidePolys[j][0], polyInput.m_insidePolys[j].size());
325  if (area < 0)
326  {
327  std::reverse(polyInput.m_insidePolys[j].begin(), polyInput.m_insidePolys[j].end());
328  }
329  }
330  }
331 } // MeMultiPolyMesherImpl::EnsureProperPolygonInputs
332 //------------------------------------------------------------------------------
336 //------------------------------------------------------------------------------
338 {
339  std::string errors;
340  if (a_io.m_polys.empty())
341  {
342  errors += "Error: Per polygon input is empty. No polygons to mesh.\n";
343  }
344  else
345  {
346  for (size_t i = 0; i < a_io.m_polys.size(); ++i)
347  {
348  const MePolyInput& polyInput = a_io.m_polys[i];
349  std::string id;
350  {
351  std::stringstream ss;
352  if (polyInput.m_polyId > -1)
353  ss << "id " << polyInput.m_polyId;
354  else
355  ss << "index " << i;
356  id = ss.str();
357  }
358 
359  // Check outer polygon size
360  if (polyInput.m_outPoly.empty())
361  {
362  std::stringstream ss;
363  ss << "Error: Outer polygon " << id << " is empty.\n";
364  errors += ss.str();
365  }
366 
367  // Check inner polygons (if any)
368  for (size_t j = 0; j < polyInput.m_insidePolys.size(); ++j)
369  {
370  if (polyInput.m_insidePolys[j].empty())
371  {
372  std::stringstream ss;
373  ss << "Error: Inner polygon " << j << " of outer polygon " << id << " is empty.\n";
374  errors += ss.str();
375  }
376  }
377 
378  // Check patch corners size
379  if (polyInput.m_polyCorners.size() != 0 && polyInput.m_polyCorners.size() != 3)
380  {
381  std::stringstream ss;
382  ss << "Error: Polygon patch corners for polygon " << id << " is size "
383  << polyInput.m_polyCorners.size() << ". It must be size 0 or 3.\n";
384  errors += ss.str();
385  }
386  }
387 
388  if (a_io.m_checkTopology)
389  {
390  CheckForIntersections(a_io, errors);
391  }
392  }
393 
394  if (!errors.empty())
395  {
396  XM_ASSERT(false);
397  XM_LOG(xmlog::warning, errors);
398  return false;
399  }
400  return true;
401 } // MeMultiPolyMesherImpl::ValidateInput
402 //------------------------------------------------------------------------------
410 //------------------------------------------------------------------------------
412  std::string& a_errors) const
413 {
414  // Create vectors defining all poly segments and where they're located
415 
416  std::vector<const Pt3d*> segments;
417  std::vector<SegmentLocation> segmentLocs;
418  for (int i = 0; i < (int)a_io.m_polys.size(); ++i)
419  {
420  if (i > 0)
421  {
422  // Add a null to separate polygons
423  segments.push_back(nullptr);
424  segmentLocs.push_back(SegmentLocation());
425  }
426 
427  const MePolyInput& inputPoly = a_io.m_polys[i];
428  const VecPt3d& outerPoly = inputPoly.m_outPoly;
429 
430  // Do the outer poly
431  for (int j = 0; j < (int)outerPoly.size(); ++j)
432  {
433  segments.push_back(&outerPoly[j]);
434  segmentLocs.push_back(SegmentLocation(i, j, -1, -1));
435  }
436  // Do the last segment because polygon isn't closed
437  segments.push_back(&outerPoly[0]);
438  segmentLocs.push_back(SegmentLocation(i, 0, -1, -1));
439 
440  // Do the inner polys
441 
442  if (!inputPoly.m_insidePolys.empty())
443  {
444  // Add a null to separate polygons
445  segments.push_back(nullptr);
446  segmentLocs.push_back(SegmentLocation());
447  }
448 
449  for (int k = 0; k < (int)inputPoly.m_insidePolys.size(); ++k)
450  {
451  if (k > 0)
452  {
453  // Add a null to separate polygons
454  segments.push_back(nullptr);
455  segmentLocs.push_back(SegmentLocation());
456  }
457  const VecPt3d& innerPoly = inputPoly.m_insidePolys[k];
458 
459  // Do the inner poly
460  for (int m = 0; m < (int)innerPoly.size(); ++m)
461  {
462  segments.push_back(&innerPoly[m]);
463  segmentLocs.push_back(SegmentLocation(i, -1, k, m));
464  }
465  // Do the last segment because polygon isn't closed
466  segments.push_back(&innerPoly[0]);
467  segmentLocs.push_back(SegmentLocation(i, -1, k, 0));
468  }
469  }
470 
471  // Intersect all segments with all other segments
472 
473  double xi, yi, zi1, zi2, tol(gmXyTol());
474  size_t size = segments.size();
475 
476  // Loop through every segment
477  for (size_t i = 1; i < segments.size(); ++i)
478  {
479  // Skip nulls which indicate the end of a poly
480  if (segments[i] == nullptr)
481  {
482  ++i;
483  continue;
484  }
485  const Pt3d* one1 = segments[i - 1];
486  const Pt3d* one2 = segments[i];
487  Pt3d oneMn, oneMx;
488  gmAddToExtents(*one1, oneMn, oneMx);
489  gmAddToExtents(*one2, oneMn, oneMx);
490  oneMn -= tol;
491  oneMx += tol;
492 
493  // Starting on the next segment from where we are, go to end of segments
494  for (size_t j = i + 1; j < size; ++j)
495  {
496  // Skip nulls which indicate the end of a poly
497  if (segments[j] == nullptr)
498  {
499  ++j;
500  continue;
501  }
502  const Pt3d* two1 = segments[j - 1];
503  const Pt3d* two2 = segments[j];
504  if (ExtentsOverlap(oneMn, oneMx, *two1, *two2))
505  {
506  if (gmIntersectLineSegmentsWithTol(*one1, *one2, *two1, *two2, &xi, &yi, &zi1, &zi2, tol))
507  {
508  // See if we didn't just intersect on the ends
509  if (!gmEqualPointsXY(one1->x, one1->y, xi, yi, tol) &&
510  !gmEqualPointsXY(one2->x, one2->y, xi, yi, tol))
511  {
512  // Report the intersection
513  std::stringstream ss;
514  SegmentLocation& a0 = segmentLocs[i - 1];
515  SegmentLocation& a1 = segmentLocs[i];
516  SegmentLocation& b0 = segmentLocs[j - 1];
517  SegmentLocation& b1 = segmentLocs[j];
518  std::string s;
519  if (a0.m_inPoly == -1)
520  { // First segment on an outer poly
521  if (b0.m_inPoly == -1)
522  { // Second segment on an outer poly
523  s = (boost::format("Error: Input polygon segments intersect."
524  " The segment defined by points %d and %d"
525  " of outer polygon %d"
526  " intersects with the segment defined by points %d and %d"
527  " of outer polygon %d.\n") %
528  a0.m_polySeg % a1.m_polySeg % a0.m_poly % b0.m_polySeg % b1.m_polySeg %
529  b0.m_poly)
530  .str();
531  }
532  else
533  { // Second segment on an inner poly
534  s = (boost::format("Error: Input polygon segments intersect."
535  " The segment defined by points %d and %d"
536  " of outer polygon %d"
537  " intersects with the segment defined by points %d and %d"
538  " of inner polygon %d of outer polygon %d.\n") %
539  a0.m_polySeg % a1.m_polySeg % a0.m_poly % b0.m_inPolySeg % b1.m_inPolySeg %
540  b0.m_inPoly % b0.m_poly)
541  .str();
542  }
543  }
544  else
545  { // First segment on an inner poly
546  if (b0.m_inPoly == -1)
547  { // Second segment on an outer poly
548  s = (boost::format("Error: Input polygon segments intersect."
549  " The segment defined by points %d and %d"
550  " of inner polygon %d of outer polygon %d"
551  " intersects with the segment defined by points %d and %d"
552  " of outer polygon %d.\n") %
553  a0.m_inPolySeg % a1.m_inPolySeg % a0.m_inPoly % a0.m_poly % b0.m_polySeg %
554  b1.m_polySeg % b0.m_poly)
555  .str();
556  }
557  else
558  { // Second segment on an inner poly
559  s = (boost::format("Error: Input polygon segments intersect."
560  " The segment defined by points %d and %d"
561  " of inner polygon %d of outer polygon %d"
562  " intersects with the segment defined by points %d and %d"
563  " of inner polygon %d of outer polygon %d.\n") %
564  a0.m_inPolySeg % a1.m_inPolySeg % a0.m_inPoly % a0.m_poly % b0.m_inPolySeg %
565  b1.m_inPolySeg % b0.m_inPoly % b0.m_poly)
566  .str();
567  }
568  }
569  a_errors += s;
570  }
571  }
572  }
573  }
574  }
575 } // MeMultiPolyMesherImpl::CheckForIntersections
576 //------------------------------------------------------------------------------
584 //------------------------------------------------------------------------------
586  const Pt3d& a_oneMx,
587  const Pt3d& a_two1,
588  const Pt3d& a_two2) const
589 {
590  Pt3d twoMn, twoMx;
591  gmAddToExtents(a_two1, twoMn, twoMx);
592  gmAddToExtents(a_two2, twoMn, twoMx);
593  if (twoMn.x > a_oneMx.x)
594  {
595  return false;
596  }
597  if (twoMx.x < a_oneMn.x)
598  {
599  return false;
600  }
601  if (twoMn.y > a_oneMx.y)
602  {
603  return false;
604  }
605  if (twoMx.y < a_oneMn.y)
606  {
607  return false;
608  }
609  return true;
610 } // MeMultiPolyMesherImpl::ExtentsOverlap
611 //------------------------------------------------------------------------------
617 //------------------------------------------------------------------------------
619  const VecInt& a_triangles,
620  VecInt& a_cells)
621 {
622  // These correspond to defines in vtkCellType. There are classes downstream from
623  // meshing that convert a cell stream into a vtkUnstructured grid that use these
624  // magic numbers.
625  const int VTK_TRI(5);
626 
627  VecInt cells;
628  cells.swap(a_cells);
629  int cellCount = 0;
630  if (!a_triangles.empty())
631  {
632  cellCount = (int)a_triangles.size() / 3;
633  cells.reserve(cellCount * 5);
634  for (size_t i = 0; i < a_triangles.size(); i += 3)
635  {
636  cells.push_back(VTK_TRI);
637  cells.push_back(3);
638  cells.push_back(a_triangles[i + 0]);
639  cells.push_back(a_triangles[i + 1]);
640  cells.push_back(a_triangles[i + 2]);
641  }
642  }
643  else
644  {
645  size_t i = 0;
646  while (i < cells.size())
647  {
648  cellCount++;
649  i++; // celltype = m_cells[i++];
650  int npts = cells[i++];
651  i += npts;
652  }
653  }
654  m_cellCount += cellCount;
655 
656  if ((*m_pts).empty())
657  {
658  std::pair<std::pair<double, double>, int> pd;
659  for (size_t i = 0; i < a_points.size(); ++i)
660  {
661  pd.first.first = a_points[i].x;
662  pd.first.second = a_points[i].y;
663  pd.second = (int)i;
664  m_ptHash.insert(pd);
665  }
666  // First time, just add points to m_pts and return
667  m_pts->swap(a_points);
668  m_cells.swap(cells);
669  return;
670  }
671 
672  VecInt oldDups, newDups;
673  int oldNumPts = (int)(*m_pts).size();
674  AddUniquePoints(a_points, oldDups, newDups);
675 
676  RenumberNewMesh(oldNumPts, a_points.size(), oldDups, newDups, cells);
677  AppendNewCells(cells);
678 } // MeMultiPolyMesherImpl::AppendMesh
679 //------------------------------------------------------------------------------
688 //------------------------------------------------------------------------------
690  VecInt& a_oldDups,
691  VecInt& a_newDups)
692 {
693  a_oldDups.clear();
694  a_newDups.clear();
695  XM_ENSURE_TRUE_VOID_NO_ASSERT(!a_points.empty());
696 
697  // const double tol = 1e-9;
698  // int idxOld;
699  std::pair<std::pair<double, double>, int> pd;
700  auto itEnd = m_ptHash.end();
701  for (size_t i = 0; i < a_points.size(); ++i)
702  {
703  pd.first.first = a_points[i].x;
704  pd.first.second = a_points[i].y;
705  pd.second = (int)m_pts->size();
706  auto it = m_ptHash.find(pd.first);
707  if (it == itEnd)
708  {
709  m_ptHash.insert(pd);
710  m_pts->push_back(a_points[i]);
711  }
712  else
713  {
714  a_oldDups.push_back(it->second);
715  a_newDups.push_back((int)i);
716  }
717  }
718 } // MeMultiPolyMesherImpl::AddUniquePoints
719 //------------------------------------------------------------------------------
727 //------------------------------------------------------------------------------
729  size_t a_numNewPts,
730  const VecInt& a_oldDups,
731  const VecInt& a_newDups,
732  VecInt& a_cells) const
733 {
734  XM_ENSURE_TRUE_VOID_NO_ASSERT(a_oldDups.size() == a_newDups.size());
735 
736  // Renumber the new points starting at the old number of points
737  VecInt newPtsIdx(a_numNewPts, -1);
738  if (a_newDups.empty())
739  {
740  std::iota(newPtsIdx.begin(), newPtsIdx.end(), a_oldNumPts);
741  }
742  else
743  {
744  // Set the new points that are duplicates equal to the old point indices
745  for (size_t i = 0; i < a_newDups.size(); ++i)
746  {
747  newPtsIdx[a_newDups[i]] = a_oldDups[i];
748  }
749 
750  // Renumber, skipping duplicate points we just assigned
751  int cnt(a_oldNumPts);
752  for (size_t i = 0; i < newPtsIdx.size(); ++i)
753  {
754  if (newPtsIdx[i] < 0)
755  {
756  newPtsIdx[i] = cnt;
757  ++cnt;
758  }
759  }
760  }
761 
762  // Fix the cell numbers to correspond to the new point numbers
763  for (size_t i = 1; i < a_cells.size(); ++i)
764  {
765  int nPts = a_cells[i];
766  for (int j = 1; j <= nPts; ++j)
767  {
768  a_cells[i + j] = newPtsIdx[a_cells[i + j]];
769  }
770  i += (1 + nPts);
771  }
772 } // MeMultiPolyMesherImpl::RenumberNewMesh
773 //------------------------------------------------------------------------------
776 //------------------------------------------------------------------------------
778 {
779  m_cells.insert(m_cells.end(), a_cells.begin(), a_cells.end());
780 } // MeMultiPolyMesherImpl::AppendNewCells
781 //------------------------------------------------------------------------------
786 //------------------------------------------------------------------------------
788  const VecPt3d& a_usedPts)
789 {
790  SetPt3d setPts(a_usedPts.begin(), a_usedPts.end());
791  SetPt3d::iterator itEnd = setPts.end();
792  VecStr strLoc;
793  for (size_t i = 0; i < a_io.m_refPts.size(); ++i)
794  {
795  if (setPts.find(a_io.m_refPts[i].m_pt) == itEnd)
796  {
797  std::stringstream ss;
798  ss << "(" << a_io.m_refPts[i].m_pt.x << ", " << a_io.m_refPts[i].m_pt.y << ")";
799  strLoc.push_back(ss.str());
800  }
801  }
802  if (!strLoc.empty())
803  {
804  std::string msg =
805  "The following refine points were not included by the meshing process "
806  "because the points are located outside of all polygons.";
807  for (size_t i = 0; i < strLoc.size(); ++i)
808  {
809  msg += "\n" + strLoc[i];
810  }
811  XM_LOG(xmlog::warning, msg);
812  }
813 } // ReportUnusedRefinePts
814 
819 //------------------------------------------------------------------------------
822 //------------------------------------------------------------------------------
823 BSHP<MeMultiPolyMesher> MeMultiPolyMesher::New()
824 {
825  BSHP<MeMultiPolyMesher> ret(new MeMultiPolyMesherImpl);
826  return ret;
827 } // MePolysToUGrid::New
828 
829 } // namespace xms
830 
831 #if CXX_TEST
832 // UNIT TESTS
835 
837 
839 
840 //----- Namespace declaration --------------------------------------------------
841 
842 // namespace xms {
843 using namespace xms;
844 
849 //------------------------------------------------------------------------------
851 //------------------------------------------------------------------------------
853 {
854  BSHP<MeMultiPolyMesher> m = MeMultiPolyMesher::New();
855  TS_ASSERT(m);
856 } // MeMultiPolyMesherUnitTests::testCreateClass
857 //------------------------------------------------------------------------------
867 //------------------------------------------------------------------------------
869 {
870  MeMultiPolyMesherIo input;
871  input.m_checkTopology = true;
872  {
873  MePolyInput poly;
874  poly.m_outPoly = {{0, 0, 0}, {100, 0, 0}, {100, 10, 0}, {0, -10, 0}};
875  input.m_polys.push_back(poly);
876  }
877 
878  // Mesh them
879  BSHP<MeMultiPolyMesher> mesher = MeMultiPolyMesher::New();
880  bool asserting = xmAsserting();
881  xmAsserting() = false;
882  bool rv = mesher->MeshIt(input);
883  xmAsserting() = asserting;
884  TS_ASSERT_EQUALS(rv, false);
885  std::string errors = XmLog::Instance().GetAndClearStackStr();
886  std::string expected =
887  "---Error: Input polygon segments intersect. The segment defined by points 0 and 1 of outer "
888  "polygon 0 intersects with the segment defined by points 2 and 3 of outer polygon 0.\n"
889  "\n\n";
890 
891  TS_ASSERT_EQUALS(expected, errors);
892 } // MeMultiPolyMesherUnitTests::testCheckForIntersections1
893 //------------------------------------------------------------------------------
905 //------------------------------------------------------------------------------
907 {
908  MeMultiPolyMesherIo input;
909  input.m_checkTopology = true;
910  {
911  MePolyInput poly;
912  poly.m_outPoly = {{0, 0, 0}, {100, 0, 0}, {100, 100, 0}, {0, 100, 0}};
913  poly.m_insidePolys.push_back({{10, 50}, {90, 50}, {90, 90}, {10, 10}});
914  input.m_polys.push_back(poly);
915  }
916 
917  // Mesh them
918  BSHP<MeMultiPolyMesher> mesher = MeMultiPolyMesher::New();
919  bool asserting = xmAsserting();
920  xmAsserting() = false;
921  bool rv = mesher->MeshIt(input);
922  xmAsserting() = asserting;
923  TS_ASSERT_EQUALS(rv, false);
924  std::string errors = XmLog::Instance().GetAndClearStackStr();
925  std::string expected =
926  "---Error: Input polygon segments intersect. The segment defined by points 0 and 1 of inner "
927  "polygon 0 of outer polygon 0 intersects with the segment defined by points 2 and 3 of inner "
928  "polygon 0 of outer polygon 0.\n"
929  "\n\n";
930 
931  TS_ASSERT_EQUALS(expected, errors);
932 } // MeMultiPolyMesherUnitTests::testCheckForIntersections2
933 //------------------------------------------------------------------------------
944 //------------------------------------------------------------------------------
946 {
947  MeMultiPolyMesherIo input;
948  input.m_checkTopology = true;
949  {
950  MePolyInput poly;
951  poly.m_outPoly = {{0, 0, 0}, {100, 0, 0}, {100, 100, 0}, {0, 100, 0}};
952  poly.m_insidePolys.push_back({{90, 10}, {110, 10}, {110, 20}, {90, 20}});
953  input.m_polys.push_back(poly);
954  }
955 
956  // Mesh them
957  BSHP<MeMultiPolyMesher> mesher = MeMultiPolyMesher::New();
958  bool asserting = xmAsserting();
959  xmAsserting() = false;
960  bool rv = mesher->MeshIt(input);
961  xmAsserting() = asserting;
962  TS_ASSERT_EQUALS(rv, false);
963  std::string errors = XmLog::Instance().GetAndClearStackStr();
964  std::string expected =
965  "---Error: Input polygon segments intersect. The segment defined by points 1 and 2 of outer "
966  "polygon 0 intersects with the segment defined by points 0 and 1 of inner polygon 0 of outer "
967  "polygon 0.\n"
968  "Error: Input polygon segments intersect. The segment defined by points 1 and 2 of outer "
969  "polygon 0 intersects with the segment defined by points 2 and 3 of inner polygon 0 of outer "
970  "polygon 0.\n"
971  "\n\n";
972 
973  TS_ASSERT_EQUALS(expected, errors);
974 } // MeMultiPolyMesherUnitTests::testCheckForIntersections3
975 //------------------------------------------------------------------------------
988 //------------------------------------------------------------------------------
990 {
991  MeMultiPolyMesherIo input;
992  input.m_checkTopology = true;
993  {
994  MePolyInput poly;
995  poly.m_outPoly = {{0, 0, 0}, {0, 100, 0}, {100, 100, 0}, {100, 0, 0}};
996  input.m_polys.push_back(poly);
997  poly.m_outPoly = {{10, 10, 0}, {10, 110, 0}, {110, 110, 0}, {110, 10, 0}};
998  input.m_polys.push_back(poly);
999  }
1000 
1001  // Mesh them
1002  BSHP<MeMultiPolyMesher> mesher = MeMultiPolyMesher::New();
1003  bool asserting = xmAsserting();
1004  xmAsserting() = false;
1005  bool rv = mesher->MeshIt(input);
1006  xmAsserting() = asserting;
1007  TS_ASSERT_EQUALS(rv, false);
1008  std::string errors = XmLog::Instance().GetAndClearStackStr();
1009  std::string expected =
1010  "---Error: Input polygon segments intersect. The segment defined by points 1 and 2 of outer "
1011  "polygon 0 intersects with the segment defined by points 0 and 1 of outer polygon 1.\n"
1012  "Error: Input polygon segments intersect. The segment defined by points 2 and 3 of outer "
1013  "polygon 0 intersects with the segment defined by points 3 and 0 of outer polygon 1.\n"
1014  "\n\n";
1015 
1016  TS_ASSERT_EQUALS(expected, errors);
1017 } // MeMultiPolyMesherUnitTests::testCheckForIntersections4
1018 //------------------------------------------------------------------------------
1031 //------------------------------------------------------------------------------
1033 {
1034  MeMultiPolyMesherIo input;
1035  input.m_checkTopology = true;
1036  {
1037  MePolyInput poly;
1038  poly.m_outPoly = {{0, 0, 0}, {100, 0, 0}, {100, 100, 0}, {0, 100, 0}};
1039  poly.m_insidePolys.push_back({{10, 10}, {60, 10}, {60, 60}, {10, 60}});
1040  poly.m_insidePolys.push_back({{40, 40}, {90, 40}, {90, 90}, {40, 90}});
1041  input.m_polys.push_back(poly);
1042  }
1043 
1044  // Mesh them
1045  BSHP<MeMultiPolyMesher> mesher = MeMultiPolyMesher::New();
1046  bool asserting = xmAsserting();
1047  xmAsserting() = false;
1048  bool rv = mesher->MeshIt(input);
1049  xmAsserting() = asserting;
1050  TS_ASSERT_EQUALS(rv, false);
1051  std::string errors = XmLog::Instance().GetAndClearStackStr();
1052  std::string expected =
1053  "---Error: Input polygon segments intersect. The segment defined by points 1 and 2 of inner "
1054  "polygon 0 of outer polygon 0 intersects with the segment defined by points 0 and 1 of inner "
1055  "polygon 1 of outer polygon 0.\n"
1056  "Error: Input polygon segments intersect. The segment defined by points 2 and 3 of inner "
1057  "polygon 0 of outer polygon 0 intersects with the segment defined by points 3 and 0 of inner "
1058  "polygon 1 of outer polygon 0.\n"
1059  "\n\n";
1060 
1061  TS_ASSERT_EQUALS(expected, errors);
1062 } // MeMultiPolyMesherUnitTests::testCheckForIntersections5
1063 
1064 //} // namespace xms
1065 
1066 #endif // CXX_TEST
void testCheckForIntersections5()
Tests checking for bad input: 2 inner polys overlap.
#define XM_LOG(A, B)
bool gmIntersectLineSegmentsWithTol(const Pt3d &one1, const Pt3d &one2, const Pt3d &two1, const Pt3d &two2, double *xi, double *yi, double *zi1, double *zi2, double tol)
int m_poly
Index of polygon in MeMultiPolyMesherIo::m_polys.
Creates a mesh from multiple polygons that will honor polygon boundaries.
std::vector< int > VecInt
void AddUniquePoints(const VecPt3d &a_points, VecInt &a_oldDups, VecInt &a_newDups)
Adds new mesh points that aren&#39;t in old mesh, to old mesh.
virtual void CheckForIntersections(const MeMultiPolyMesherIo &a_io, std::string &a_errors) const override
Checks input to see if anything intersects. Will not catch polys entirely inside or outside of where ...
std::vector< MePolyInput > m_polys
Required (but some data is optional). Inputs for each polygon.
int m_polySeg
Index of segment in MePolyInput::m_outPoly.
boost::unordered_map< std::pair< double, double >, int > m_ptHash
hashes points
VecPt3d2d m_insidePolys
Optional. Inner polygons (holes). Counter clockwise. 1st pt != last.
void testCheckForIntersections3()
Tests checking for bad input: inner poly intersects outer poly.
bool ExtentsOverlap(const Pt3d &oneMn, const Pt3d &oneMx, const Pt3d &two1, const Pt3d &two2) const
Given extents of one segment, and another segment, return true if the extents of the two segments ove...
std::string STRstd(double a_value, int a_n, int width, int flags)
bool gmEqualPointsXY(double x1, double y1, double x2, double y2, double tolerance)
std::vector< std::string > VecStr
bool & xmAsserting()
bool m_checkTopology
Optional. If true, checks polygon input topology for errors.
int m_cellCount
Number of cells.
void RenumberNewMesh(int a_oldNumPts, size_t a_numNewPts, const VecInt &a_oldDups, const VecInt &a_newDups, VecInt &a_cells) const
Renumber points referred to in the new mesh to remove duplicate points and to start new mesh numberin...
void AppendNewCells(const VecInt &a_cells)
Append the new cells to the existing cells.
Struct defining the location of a polygon line segment in the list of polygons in MeMultiPolyMesherIo...
void testCreateClass()
tests creating the class
std::set< Pt3d > SetPt3d
VecInt m_cellPolygons
Polygon index of each cell.
int m_inPoly
Index of inner polygon in MePolyInput::m_insidePolys.
void EnsureProperPolygonInputs(MeMultiPolyMesherIo &a_io)
Remove last point of polygon if it is the same as the first point and make sure the polygon points ar...
bool ValidateInput(const MeMultiPolyMesherIo &a_io)
Make sure the input makes sense.
void testCheckForIntersections1()
Tests checking for bad input: self-intersecting outer poly.
void AppendMesh(VecPt3d &a_points, const VecInt &a_triangles, VecInt &a_cells)
Adds new points and triangles to existing mesh, hashing points and renumbering.
std::string GetAndClearStackStr()
#define XM_ENSURE_TRUE(...)
std::vector< MeRefinePoint > m_refPts
Optional. Refine points.
#define XM_ENSURE_TRUE_VOID_NO_ASSERT(...)
VecPt3d m_outPoly
Required. Outer polygons. Clockwise. 1st pt != last.
VecPt3d m_points
The points of the resulting mesh.
bool m_returnCellPolygons
If true, returns the polygon index of each cell.
#define XM_ASSERT(x)
void ReportUnusedRefinePts(const MeMultiPolyMesherIo &a_io, const VecPt3d &a_usedPts)
Reports refine points that were not used.
Provides the input to meshing multiple polygons and holds the output.
int m_inPolySeg
Index of segment on inner polygon.
static boost::shared_ptr< MePolyMesher > New()
Creates a polymesher class.
SegmentLocation(int a_poly=0, int a_polySeg=0, int a_inPoly=0, int a_inPolySeg=0)
Constructor.
BSHP< VecPt3d > m_pts
Mesh points. BSHP because of PtSearch::VectorThatGrowsToSearch.
double gmPolygonArea(const Pt3d *pts, size_t npoints)
static XmLog & Instance(bool a_delete=false, XmLog *a_new=NULL)
static boost::shared_ptr< MeMultiPolyMesher > New()
Creates a class.
void gmAddToExtents(const Pt3d &a_pt, Pt3d &a_min, Pt3d &a_max)
VecInt m_cells
Mesh cells as a stream.
Meshing inputs for one polygon.
void testCheckForIntersections2()
Tests checking for bad input: self-intersecting inner poly.
virtual bool MeshIt(MeMultiPolyMesherIo &a_io) override
Creates a triangle mesh from the input polygons. The polygons can not overlap.
double gmXyTol(bool a_set, double a_value)
VecInt m_cells
The cells of the resulting mesh, as a stream.
void testCheckForIntersections4()
Tests checking for bad input: 2 outer polys overlap.
std::vector< Pt3d > VecPt3d