xmsgrid  1.0
GmMultiPolyIntersectionSorterTerse.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
6 //------------------------------------------------------------------------------
7 
8 //----- Included files ---------------------------------------------------------
9 
10 // 1. Precompiled header
11 
12 // 2. My header
14 
15 // 3. Standard Library Headers
16 #include <cfloat>
17 
18 // 4. External Library Headers
19 #include <xmscore/math/math.h> // EQ_EPS
20 #include <xmscore/misc/XmError.h> // XM_*
21 #include <xmscore/misc/xmstype.h> // XM_NONE
22 #include <xmscore/stl/set.h> // Set*
23 #include <xmscore/stl/vector.h> // Vec*
24 #include <xmsgrid/geometry/GmMultiPolyIntersectorData.h> // GmMultiPolyIntersectorData
25 #include <xmsgrid/geometry/geoms.h> // gmComputeCentroid, gmTurn
26 
27 // 5. Shared Headers
28 
29 // 6. Non-shared Headers
30 
31 //----- Forward declarations ---------------------------------------------------
32 
33 //----- External globals -------------------------------------------------------
34 
35 //----- Namespace declaration --------------------------------------------------
36 
37 namespace xms {
38 //----- Constants / Enumerations -----------------------------------------------
39 
40 //----- Classes / Structs ------------------------------------------------------
41 
42 namespace { // unnamed namespace
43 } // unnamed namespace
44 
55 //------------------------------------------------------------------------------
63 //------------------------------------------------------------------------------
65  GmMultiPolyIntersectorData &a_data, std::vector<int> &polyids,
66  std::vector<double> &tvalues, std::vector<Pt3d> &a_pts, double a_tol)
67 {
68  m_d = &a_data;
69  m_tol = a_tol;
70 
73  SwapAdjacents();
74  IntersectionsToPolyIdsAndTValues(polyids, tvalues, a_pts);
75  FixArrays(polyids, tvalues, a_pts);
76 } // GmMultiPolyIntersectionSorterTerse::Sort
77 //------------------------------------------------------------------------------
81 //------------------------------------------------------------------------------
83 {
85 
86  // Loop through
87  for (size_t i = 1; i < m_d->m_ixs.size() - 1; ++i)
88  {
89  if (gmEqualPointsXY(m_d->m_ixs[i].m_pt, m_d->m_ixs[i - 1].m_pt, gmXyTol()))
90  {
91  m_d->m_ixs[i].m_t = m_d->m_ixs[i - 1].m_t;
92  }
93  }
94 
95  // Try to get 1.0 t-values as far down as possible.
96  for (size_t i = m_d->m_ixs.size() - 2; i > 0; --i)
97  {
98  if (gmEqualPointsXY(m_d->m_ixs[i].m_pt, m_d->m_ixs[i + 1].m_pt, gmXyTol()))
99  {
100  m_d->m_ixs[i].m_t = m_d->m_ixs[i + 1].m_t;
101  }
102  else
103  {
104  break;
105  }
106  }
107 } // GmMultiPolyIntersectionSorterTerse::FixTValueAtDuplicateXy
108 //------------------------------------------------------------------------------
114 //------------------------------------------------------------------------------
116 {
118 
119  // Change the t-values so that duplicate points in XY have the same t-value.
120  VecDbl oldTValues;
121  oldTValues.reserve(m_d->m_ixs.size());
122  for (size_t i = 0; i < m_d->m_ixs.size(); ++i)
123  {
124  oldTValues.push_back(m_d->m_ixs[i].m_t);
125  }
127 
128  VecInt tChange;
129  FindWhereTValuesChange(tChange);
130 
131  VecInt toRemove;
132 
133  // Loop through tChanges
134  for (size_t i = 0; i < tChange.size() - 1; ++i)
135  {
136  if (tChange[i + 1] - tChange[i] > 1)
137  {
138  VecInt inNeither;
139  FindPreviousNextNeither(tChange, (int)i, nullptr, nullptr, &inNeither);
140 
141  if ((i > 0 && i + 2 < tChange.size()) || inNeither.size() > 1 ||
142  (i == 0 && tChange[1] - tChange[0] > 1))
143  {
144  toRemove.insert(toRemove.end(), inNeither.begin(), inNeither.end());
145  }
146  }
147  }
148 
149  AddMissingEndpointIds(tChange);
150 
151  // Reset the t-values
152  for (size_t i = 0; i < m_d->m_ixs.size(); ++i)
153  {
154  m_d->m_ixs[i].m_t = oldTValues[i];
155  }
156 
157  // Remove them, working backwards
158  for (size_t i = toRemove.size(); i-- > 0;)
159  {
160  m_d->m_ixs.erase(m_d->m_ixs.begin() + toRemove[i]);
161  }
162 } // GmMultiPolyIntersectionSorterTerse::RemoveCornerTouches
163 //------------------------------------------------------------------------------
166 //------------------------------------------------------------------------------
169 
170  VecInt tChange;
171  FindWhereTValuesChange(tChange);
172 
173  VecInt toRemove;
174 
175  // Loop through tChanges
176  for (size_t i = 0; i < tChange.size() - 1; ++i) {
177  if (tChange[i + 1] - tChange[i] > 1) {
178  VecInt inNext;
179  // Get intersections in this tvalue group that also appear in next group.
180  // These are the first points of the edges.
181  FindPreviousNextNeither(tChange, (int)i, nullptr, &inNext, nullptr);
182 
183  if (inNext.size() > 1) {
184  // Get intersections in next tvalue group that match those in current.
185  // These are the last points of the edges.
186  VecInt inPrev;
187  FindPreviousNextNeither(tChange, (int)i + 1, &inPrev, nullptr, nullptr);
188  // XM_ASSERT(inPrev.size() == inNext.size());
189  // Not sure why the assert above was put in. Even when it asserts,
190  // we still seem to get good results in the end.
191 
192  // Keep only the polygons that are on the right of the line
193  bool found = false; // Should be just 2 so stop if we find one
194  for (size_t j = 0; j < inNext.size() && !found; ++j) {
195  // Find the other end of this edge
196  int edgeBeg = inNext[j]; // index into m_d->m_ixs of beginning of edge
197  int edgeEnd = XM_NONE; // index into m_d->m_ixs of end of edge
198  for (size_t k = 0; k < inPrev.size() && edgeEnd == XM_NONE; ++k) {
199  if (m_d->m_ixs[inPrev[k]].m_i == m_d->m_ixs[edgeBeg].m_i)
200  edgeEnd = inPrev[k];
201  }
202  XM_ASSERT(edgeEnd != XM_NONE);
203 
204  int polyIdx = m_d->m_ixs[edgeBeg].m_i - 1;
205  // Create the poly as a vector of Pt3ds for gmComputeCentroid
206  VecPt3d poly;
207  for (size_t k = 0; k < m_d->m_polys[polyIdx].size(); ++k) {
208  poly.push_back(m_d->m_points[m_d->m_polys[polyIdx][k]]);
209  }
210  Pt3d centroid = gmComputeCentroid(poly);
211  Turn_enum turn = gmTurn(m_d->m_ixs[edgeBeg].m_pt,
212  m_d->m_ixs[edgeEnd].m_pt, centroid);
213  if (turn != TURN_RIGHT) {
214  toRemove.push_back(edgeBeg);
215  toRemove.push_back(edgeEnd);
216  found = true;
217  }
218  }
219  }
220  }
221  }
222 
223  // Remove them, working backwards
224  std::sort(toRemove.begin(), toRemove.end());
225  for (size_t i = toRemove.size(); i-- > 0;) {
226  m_d->m_ixs.erase(m_d->m_ixs.begin() + toRemove[i]);
227  }
228 } // GmMultiPolyIntersectionSorterTerse::RemoveDuplicateEdges
229 //------------------------------------------------------------------------------
231 //------------------------------------------------------------------------------
234 
235  // 2 intersections
236  if (m_d->m_ixs.size() == 2) {
237  if ((!m_d->m_polys1.empty() &&
238  m_d->m_polys1.find(m_d->m_ixs[0].m_i) == m_d->m_polys1.end()) ||
239  (!m_d->m_polys2.empty() &&
240  m_d->m_polys2.find(m_d->m_ixs[1].m_i) == m_d->m_polys2.end())) {
241  Swap(0, 1);
242  }
243  return;
244  }
245 
246  // 3 or more intersections
247  int i = 1;
248  // Do first one outside of loop
249  if (EQ_EPS(m_d->m_ixs[1].m_t, m_d->m_ixs[0].m_t, FLT_EPSILON) &&
250  (m_d->m_ixs[0].m_i == m_d->m_ixs[2].m_i ||
251  (m_d->m_ixs.size() > 3 && m_d->m_ixs[0].m_i == m_d->m_ixs[3].m_i))) {
252  Swap(0, 1);
253  ++i;
254  }
255 
256  for (; i < (int)m_d->m_ixs.size(); ++i) {
257  if (EQ_EPS(m_d->m_ixs[i].m_t, m_d->m_ixs[i - 1].m_t, FLT_EPSILON)) {
258  // look back
259  if (i - 2 >= 0 && m_d->m_ixs[i].m_i == m_d->m_ixs[i - 2].m_i) {
260  Swap(i, i - 1);
261  }
262  }
263  }
264 } // GmMultiPolyIntersectionSorterTerse::SwapAdjacents
265 //------------------------------------------------------------------------------
268 //------------------------------------------------------------------------------
270 {
271  XM_ENSURE_TRUE_NO_ASSERT(a_tChange.size() >= 2);
273 
274  // make sure all starting point polygons are included
275  if (m_d->m_ixs[0].m_t == 0.0)
276  {
277  int begin = a_tChange[0];
278  int end = a_tChange[1];
279  for (int i = begin; i < end; ++i)
280  {
281  m_d->m_polys1.insert(m_d->m_ixs[i].m_i);
282  }
283  }
284 
285  // make sure all ending point polygons are included
286  if (m_d->m_ixs.back().m_t == 1.0)
287  {
288  int begin = a_tChange[a_tChange.size() - 2];
289  int end = a_tChange.back();
290  for (int i = begin; i < end; ++i)
291  {
292  m_d->m_polys2.insert(m_d->m_ixs[i].m_i);
293  }
294  }
295 } // GmMultiPolyIntersectionSorterTerse::AddMissingEndpointIds
296 //------------------------------------------------------------------------------
302 //------------------------------------------------------------------------------
304  VecInt &polyids, VecDbl &tvalues, std::vector<Pt3d> &a_pts) const {
305  if (m_d->m_ixs.size() == 2) {
306  if (EQ_EPS(m_d->m_ixs[0].m_t, m_d->m_ixs[1].m_t, FLT_EPSILON)) {
307  if (!m_d->m_polys1.empty() && !m_d->m_polys2.empty() &&
308  m_d->m_polys1 != m_d->m_polys2) {
309  // test2InIn. Combine 2 into 1 getting entrance into second poly
310  if (m_d->m_polys1.find(m_d->m_ixs[0].m_i) != m_d->m_polys1.end()) {
311  AddToPolyIdsAndTValues(m_d->m_ixs[1], polyids, tvalues, a_pts);
312  } else {
313  AddToPolyIdsAndTValues(m_d->m_ixs[0], polyids, tvalues, a_pts);
314  }
315  } else {
316  AddToPolyIdsAndTValues(m_d->m_ixs[0], polyids, tvalues, a_pts);
317  AddToPolyIdsAndTValues(m_d->m_ixs[1], polyids, tvalues, a_pts);
318  }
319  } else {
320  AddToPolyIdsAndTValues(m_d->m_ixs[0], polyids, tvalues, a_pts);
321  AddToPolyIdsAndTValues(m_d->m_ixs[1], polyids, tvalues, a_pts);
322  }
323  return;
324  }
325 } // GmMultiPolyIntersectionSorterTerse::IntersectionsToPolyIdsAndTValuesFor2
326 //------------------------------------------------------------------------------
332 //------------------------------------------------------------------------------
335  std::vector<Pt3d> &a_pts) const {
337 
338  // Mark where we have a change in t values
339  VecInt tChange;
340  FindWhereTValuesChange(tChange);
341 
342  SetInt setIds, setIdsPrev; // ids we're adding now, & that we added previously
343  // Loop through tChanges
344  for (int i = 0; i < (int)tChange.size() - 1; ++i) {
345  if (tChange[i + 1] - tChange[i] > 1) {
346  VecInt inPrev, inNext, inNeither;
347  FindPreviousNextNeither(tChange, i, &inPrev, &inNext, &inNeither);
348 
349  if (i == 0) {
350  // If we don't have any in "inNeither", use next as inNeither.
351  if (inPrev.empty() && inNeither.empty())
352  inNeither = inNext;
353 
354  // Take the ones that are not found
355  for (size_t j = 0; j < inNeither.size(); ++j) {
356  setIds.insert(m_d->m_ixs[inNeither[j]].m_i);
357  AddToPolyIdsAndTValues(m_d->m_ixs[inNeither[j]], polyids, tvalues,
358  a_pts);
359  }
360  } else {
361  // Take the ones that were found in the previous group, that were
362  // not added previously
363  for (size_t j = 0; j < inPrev.size(); ++j) {
364  if (setIdsPrev.find(m_d->m_ixs[inPrev[j]].m_i) == setIdsPrev.end()) {
365  setIds.insert(m_d->m_ixs[inPrev[j]].m_i);
366  AddToPolyIdsAndTValues(m_d->m_ixs[inPrev[j]], polyids, tvalues,
367  a_pts);
368  }
369  }
370 
371  if (setIds.empty()) {
372  // Since we haven't added any yet, add those from next that weren't
373  // added previously
374  for (size_t j = 0; j < inNext.size(); ++j) {
375  if (setIdsPrev.find(m_d->m_ixs[inNext[j]].m_i) ==
376  setIdsPrev.end()) {
377  setIds.insert(m_d->m_ixs[inNext[j]].m_i);
378  AddToPolyIdsAndTValues(m_d->m_ixs[inNext[j]], polyids, tvalues,
379  a_pts);
380  }
381  }
382  }
383 
384  // Then take the ones that were not found
385  for (size_t j = 0; j < inNeither.size(); ++j) {
386  setIds.insert(m_d->m_ixs[inNeither[j]].m_i);
387  AddToPolyIdsAndTValues(m_d->m_ixs[inNeither[j]], polyids, tvalues,
388  a_pts);
389  }
390 
391  if (setIds.empty()) {
392  // Since we haven't added any yet, add those from previous group
393  for (size_t j = 0; j < inPrev.size(); ++j) {
394  setIds.insert(m_d->m_ixs[inPrev[j]].m_i);
395  AddToPolyIdsAndTValues(m_d->m_ixs[inPrev[j]], polyids, tvalues,
396  a_pts);
397  }
398  }
399  }
400  } else {
401  setIds.insert(m_d->m_ixs[tChange[i]].m_i);
402  AddToPolyIdsAndTValues(m_d->m_ixs[tChange[i]], polyids, tvalues, a_pts);
403  }
404 
405  setIdsPrev = setIds;
406  setIds.clear();
407  }
408 
409 } // IntersectionsToPolyIdsAndTValuesFor3OrMore
410 //------------------------------------------------------------------------------
415 //------------------------------------------------------------------------------
417  VecInt &polyids, VecDbl &tvalues, std::vector<Pt3d> &a_pts) const {
418  polyids.clear();
419  tvalues.clear();
420  a_pts.clear();
421 
423 
424  // 1 intersection
425  if (m_d->m_ixs.size() == 1) {
426  polyids.push_back(m_d->m_ixs[0].m_i);
427  tvalues.push_back(m_d->m_ixs[0].m_t);
428  a_pts.push_back(m_d->m_ixs[0].m_pt);
429  return;
430  }
431 
432  // 2 intersections
433  if (m_d->m_ixs.size() == 2) {
434  IntersectionsToPolyIdsAndTValuesFor2(polyids, tvalues, a_pts);
435  return;
436  }
437 
438  // 3 or more intersections
439  IntersectionsToPolyIdsAndTValuesFor3OrMore(polyids, tvalues, a_pts);
440 
441 } // GmMultiPolyIntersectionSorterTerse::IntersectionsToPolyIdsAndTValues
442 //------------------------------------------------------------------------------
448 //------------------------------------------------------------------------------
450  VecDbl &tvalues,
451  VecPt3d &a_pts) const {
452  XM_ENSURE_TRUE_VOID_NO_ASSERT(!polyids.empty());
453 
454  // Make sure we have at least two values - an entrance and exit
455  if (polyids.size() == 1) {
456  // Only one intersection. Add an exit that is the same
457  polyids.push_back(polyids.back());
458  tvalues.push_back(tvalues.back());
459  a_pts.push_back(a_pts.back());
460  }
461 
462  // If crossing a vertex, make sure we have an exit
463  if (m_d->m_polys2.empty() && polyids.back() != polyids[polyids.size() - 2]) {
464  polyids.push_back(polyids.back());
465  tvalues.push_back(tvalues.back());
466  a_pts.push_back(a_pts.back());
467  }
468 
469  // Make sure we always end with -1
470  if (polyids.size() > 1) {
471  polyids.back() = -1;
472  }
473 
474  // If we leave and reenter, set exit to -1
475  for (size_t i = 1; i < polyids.size(); ++i) {
476  if (polyids[i] == polyids[i - 1]) {
477  polyids[i] = -1;
478  }
479  }
480 
481 } // GmMultiPolyIntersectionSorterTerse::FixArrays
482 //------------------------------------------------------------------------------
489 //------------------------------------------------------------------------------
491  const ix &a_ix, VecInt &polyids, VecDbl &tvalues,
492  std::vector<Pt3d> &a_pts) const {
493  polyids.push_back(a_ix.m_i);
494  tvalues.push_back(a_ix.m_t);
495  a_pts.push_back(a_ix.m_pt);
496 } // GmMultiPolyIntersectionSorterTerse::AddToPolyIdsAndTValues
497 //------------------------------------------------------------------------------
509 //------------------------------------------------------------------------------
511  const VecInt &tChange, const int idx, VecInt *inPrev, VecInt *inNext,
512  VecInt *inNeither) const {
513  if (inPrev)
514  inPrev->clear();
515  if (inNext)
516  inNext->clear();
517  if (inNeither)
518  inNeither->clear();
519  for (int j = tChange[idx]; j < tChange[idx + 1]; ++j) {
520  // Corners at the start of the list of intersections appear the same
521  // as edges so we can't remove them because we don't have a previous
522  // set of tvalues to look at.
523  bool foundPrev = false;
524  if (idx > 0) {
525  for (int k = tChange[idx - 1]; k < tChange[idx] && !foundPrev; ++k) {
526  if (m_d->m_ixs[j].m_i == m_d->m_ixs[k].m_i) {
527  foundPrev = true;
528  }
529  }
530  }
531 
532  // Corners at the end of the list of intersections appear the same
533  // as edges so we can't remove them because we don't have a inNext set
534  // of tvalues to look at.
535  bool foundNext = false;
536  if (!foundPrev && idx + 2 < (int)tChange.size()) {
537  for (int k = tChange[idx + 1]; k < (int)tChange[idx + 2] && !foundNext;
538  ++k) {
539  if (m_d->m_ixs[j].m_i == m_d->m_ixs[k].m_i) {
540  foundNext = true;
541  }
542  }
543  }
544 
545  if (foundPrev) {
546  if (inPrev)
547  inPrev->push_back(j);
548  } else if (foundNext) {
549  if (inNext)
550  inNext->push_back(j);
551  } else {
552  if (inNeither)
553  inNeither->push_back(j);
554  }
555  }
556 } // GmMultiPolyIntersectionSorterTerse::FindPreviousNextNeither
557 //------------------------------------------------------------------------------
561 //------------------------------------------------------------------------------
563  ix temp = m_d->m_ixs[a];
564  m_d->m_ixs[a] = m_d->m_ixs[b];
565  m_d->m_ixs[b] = temp;
566 } // GmMultiPolyIntersectionSorterTerse::Swap
567 //------------------------------------------------------------------------------
570 //------------------------------------------------------------------------------
572  VecInt &tChange) const {
573  tChange.assign(1, 0);
574  for (size_t i = 1; i < m_d->m_ixs.size(); ++i) {
575  if (!EQ_TOL(m_d->m_ixs[i].m_t, m_d->m_ixs[i - 1].m_t, m_tol)) {
576  tChange.push_back((int)i);
577  }
578  }
579  tChange.push_back((int)m_d->m_ixs.size()); // for convenience
580 } // GmMultiPolyIntersectionSorterTerse::FindWhereTValuesChange
581 
582 } // namespace xms
std::vector< std::vector< int > > m_polys
0-based? indices into m_points to form polygons
void SwapAdjacents()
Swap adjacent identical intersections if necessary.
std::vector< int > VecInt
#define XM_NONE
std::vector< double > VecDbl
GmMultiPolyIntersectorData * m_d
Intersection data from GmMultiPolyIntersector.
bool gmEqualPointsXY(double x1, double y1, double x2, double y2, double tolerance)
Returns true if the points are equal to within gmXyTol().
Definition: geoms.cpp:1308
Turn_enum gmTurn(const Pt3d &a_v1, const Pt3d &a_v2, const Pt3d &a_v3, double a_angtol)
Determine if angle a_v1, a_v2, a_v3 is a left turn, a right turn, colinear 180 degrees, or colinear 0 degrees.
Definition: geoms.cpp:863
#define XM_ENSURE_TRUE_NO_ASSERT(...)
double m_tol
Tolerance used when comparing t values.
void FindPreviousNextNeither(const std::vector< int > &tChange, const int idx, std::vector< int > *inPrev, std::vector< int > *inNext, std::vector< int > *inNeither) const
Look at each poly in this set of t values and see if it exists in the previous or next set of t value...
An intersection point of a line with a polygon.
std::set< int > m_polys2
polygon IDs (1-based) that 2nd point is inside on on
void RemoveCornerTouches()
Remove polys that only touch the line at a corner. We try to identify these corner polys using the fo...
turn right
Definition: geoms.h:30
void FindWhereTValuesChange(std::vector< int > &tChange) const
Mark where we have a change in t values.
Pt3d m_pt
Intersection location.
int m_i
The polygon id (1 based)
void IntersectionsToPolyIdsAndTValuesFor3OrMore(std::vector< int > &polyids, std::vector< double > &tvalues, std::vector< Pt3d > &a_pts) const
Copy intersections to polyids and tvalues when there are 3 or more intersections. ...
double m_t
t values
void IntersectionsToPolyIdsAndTValuesFor2(std::vector< int > &polyids, std::vector< double > &tvalues, std::vector< Pt3d > &a_pts) const
Copy intersections to polyids and tvalues when there are 2 intersections.
Functions dealing with geometry.
void AddMissingEndpointIds(const std::vector< int > &a_tChange)
Add endpoint polygon IDs that may be removed as duplicates.
std::set< int > m_polys1
polygon IDs (1-based) that 1st point is inside or on
#define XM_ENSURE_TRUE_VOID_NO_ASSERT(...)
std::set< int > SetInt
Pt3d gmComputeCentroid(const VecPt3d &a_points)
Computes the centroid of points (but not of a polygon). Shouldn&#39;t pass an empty vector (no checks are...
Definition: geoms.cpp:895
void AddToPolyIdsAndTValues(const ix &a_ix, std::vector< int > &polyids, std::vector< double > &tvalues, std::vector< Pt3d > &a_pts) const
Add the intersection info to the arrays.
double gmXyTol(bool a_set, double a_value)
Get or set (set first time) global xy tolerance for float operations.
Definition: geoms.cpp:1279
bool EQ_TOL(const _T &A, const _U &B, const _V &tolerance)
std::vector< ix > m_ixs
Intersections.
#define XM_ASSERT(x)
void FixArrays(std::vector< int > &polyids, std::vector< double > &tvalues, std::vector< Pt3d > &a_pts) const
Make sure we always have at least two tvalues, an entrance and an exit, and that the last polyid = -1...
void FixTValueAtDuplicateXy()
The t-value at the same point should be the same, but it can be off in the floating point representat...
Struct used by GmMultiPolyIntersector.
virtual void Sort(GmMultiPolyIntersectorData &a_data, std::vector< int > &polyids, std::vector< double > &tvalues, std::vector< Pt3d > &a_pts, double a_tol) override
Sort the intersections, remove redundant info.
XMS Namespace.
Definition: geoms.cpp:34
void RemoveDuplicateEdges()
If the line goes along an edge we may have polygons on both sides of the edge. Perhaps we want only t...
void Swap(int a, int b)
Swap the order of the intersections a and b.
Turn_enum
direction of turn between 3 points
Definition: geoms.h:28
void IntersectionsToPolyIdsAndTValues(std::vector< int > &polyids, std::vector< double > &tvalues, std::vector< Pt3d > &a_pts) const
Copy intersections to polyids and tvalues, removing duplicates.
std::vector< Pt3d > VecPt3d
bool EQ_EPS(_T A, _U B, _V epsilon)
std::vector< Pt3d > m_points
All points used by all polygons.