GteIsPlanarGraph.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
10 #include <algorithm>
11 #include <array>
12 #include <map>
13 #include <set>
14 #include <vector>
15 
16 // Test whether an undirected graph is planar. The input positions must be
17 // unique and the input edges must be unique, so the number of positions is
18 // at least 2 and the number of edges is at least one. The elements of the
19 // edges array must be indices in {0,..,positions.size()-1}.
20 //
21 // A sort-and-sweep algorithm is used to determine edge-edge intersections.
22 // If none of the intersections occur at edge-interior points, the graph is
23 // planar. See Game Physics (2nd edition), Section 6.2.2: Culling with
24 // Axis-Aligned Bounding Boxes for such an algorithm. The operator()
25 // returns 'true' when the graph is planar. If it returns 'false', the
26 // 'invalidIntersections' set contains pairs of edges that intersect at an
27 // edge-interior point (that by definition is not a graph vertex). Each set
28 // element is a pair of indices into the 'edges' array; the indices are
29 // ordered so that element[0] < element[1]. The Real type must be chosen
30 // to guarantee exact computation of edge-edge intersections.
31 
32 namespace gte
33 {
34 
35 template <typename Real>
37 {
38 public:
39  IsPlanarGraph();
40 
41  enum
42  {
50  };
51 
52  // The function returns a combination of the IPG_* flags listed in the
53  // previous enumeration. A combined value of 0 indicates the input
54  // forms a planar graph. If the combined value is not zero, you may
55  // examine the flags for the failure conditions and use the Get* member
56  // accessors to obtain specific information about the failure. If the
57  // positions.size() < 2 or edges.size() == 0, the IPG_INVALID_INPUT_SIZES
58  // flag is set.
59 
60  int operator()(std::vector<std::array<Real, 2>> const& positions,
61  std::vector<std::array<int, 2>> const& edges);
62 
63  // A pair of indices (v0,v1) into the positions array is stored as
64  // (min(v0,v1), max(v0,v1)). This supports sorted containers of edges.
65  struct OrderedEdge
66  {
67  OrderedEdge(int v0 = -1, int v1 = -1);
68  bool operator<(OrderedEdge const& edge) const;
69  std::array<int, 2> V;
70  };
71 
72  inline std::vector<std::vector<int>> const& GetDuplicatedPositions() const;
73  inline std::vector<std::vector<int>> const& GetDuplicatedEdges() const;
74  inline std::vector<int> const& GetDegenerateEdges() const;
75  inline std::vector<int> const& GetEdgesWithInvalidVertices() const;
76  inline std::vector<OrderedEdge> const& GetInvalidIntersections() const;
77 
78 private:
79  class Endpoint
80  {
81  public:
82  Real value; // endpoint value
83  int type; // '0' if interval min, '1' if interval max.
84  int index; // index of interval containing this endpoint
85 
86  // Comparison operator for sorting.
87  bool operator<(Endpoint const& endpoint) const;
88  };
89 
90  int IsValidTopology(std::vector<std::array<Real, 2>> const& positions,
91  std::vector<std::array<int, 2>> const& edges);
92 
93  void ComputeOverlappingRectangles(std::vector<std::array<Real, 2>> const& positions,
94  std::vector<std::array<int, 2>> const& edges,
95  std::set<OrderedEdge>& overlappingRectangles) const;
96 
98  std::array<Real, 2> const& p0, std::array<Real, 2> const& p1,
99  std::array<Real, 2> const& q0, std::array<Real, 2> const& q1) const;
100 
101  std::vector<std::vector<int>> mDuplicatedPositions;
102  std::vector<std::vector<int>> mDuplicatedEdges;
103  std::vector<int> mDegenerateEdges;
104  std::vector<int> mEdgesWithInvalidVertices;
105  std::vector<OrderedEdge> mInvalidIntersections;
106  Real mZero, mOne;
107 };
108 
109 
110 template <typename Real>
112  :
113  mZero(0),
114  mOne(1)
115 {
116 }
117 
118 template <typename Real>
120  std::vector<std::array<Real, 2>> const& positions,
121  std::vector<std::array<int, 2>> const& edges)
122 {
123  mDuplicatedPositions.clear();
124  mDuplicatedEdges.clear();
125  mDegenerateEdges.clear();
127  mInvalidIntersections.clear();
128 
129  int flags = IsValidTopology(positions, edges);
130  if (flags == IPG_INVALID_INPUT_SIZES)
131  {
132  return flags;
133  }
134 
135  std::set<OrderedEdge> overlappingRectangles;
136  ComputeOverlappingRectangles(positions, edges, overlappingRectangles);
137  for (auto key : overlappingRectangles)
138  {
139  // Get the endpoints of the line segments for the edges whose bounding
140  // rectangles overlapped. Determine whether the line segments
141  // intersect. If they do, determine how they intersect.
142  std::array<int, 2> e0 = edges[key.V[0]];
143  std::array<int, 2> e1 = edges[key.V[1]];
144  std::array<Real, 2> const& p0 = positions[e0[0]];
145  std::array<Real, 2> const& p1 = positions[e0[1]];
146  std::array<Real, 2> const& q0 = positions[e1[0]];
147  std::array<Real, 2> const& q1 = positions[e1[1]];
148  if (InvalidSegmentIntersection(p0, p1, q0, q1))
149  {
150  mInvalidIntersections.push_back(key);
151  }
152  }
153 
154  if (mInvalidIntersections.size() > 0)
155  {
156  flags |= IPG_INVALID_INTERSECTIONS;
157  }
158 
159  return flags;
160 }
161 
162 template <typename Real>
163 inline std::vector<std::vector<int>> const&
165 {
166  return mDuplicatedPositions;
167 }
168 
169 template <typename Real>
170 inline std::vector<std::vector<int>> const&
172 {
173  return mDuplicatedEdges;
174 }
175 
176 template <typename Real>
177 inline std::vector<int> const&
179 {
180  return mDegenerateEdges;
181 }
182 
183 template <typename Real>
184 inline std::vector<int> const&
186 {
188 }
189 
190 template <typename Real>
191 inline std::vector<typename IsPlanarGraph<Real>::OrderedEdge> const&
193 {
194  return mInvalidIntersections;
195 }
196 
197 template <typename Real>
198 int IsPlanarGraph<Real>::IsValidTopology(std::vector<std::array<Real, 2>> const& positions,
199  std::vector<std::array<int, 2>> const& edges)
200 {
201  int const numPositions = static_cast<int>(positions.size());
202  int const numEdges = static_cast<int>(edges.size());
203  if (numPositions < 2 || numEdges == 0)
204  {
205  // The graph must have at least one edge.
207  }
208 
209  // The positions must be unique.
211  std::map<std::array<Real, 2>, std::vector<int>> uniquePositions;
212  for (int i = 0; i < numPositions; ++i)
213  {
214  std::array<Real, 2> p = positions[i];
215  auto iter = uniquePositions.find(p);
216  if (iter == uniquePositions.end())
217  {
218  std::vector<int> indices;
219  indices.push_back(i);
220  uniquePositions.insert(std::make_pair(p, indices));
221  }
222  else
223  {
224  iter->second.push_back(i);
225  }
226  }
227  if (uniquePositions.size() < positions.size())
228  {
229  // At least two positions are duplicated.
230  for (auto const& element : uniquePositions)
231  {
232  if (element.second.size() > 1)
233  {
234  mDuplicatedPositions.push_back(element.second);
235  }
236  }
237  flags |= IPG_DUPLICATED_POSITIONS;
238  }
239 
240  // The edges must be unique.
241  std::map<OrderedEdge, std::vector<int>> uniqueEdges;
242  for (int i = 0; i < numEdges; ++i)
243  {
244  OrderedEdge key(edges[i][0], edges[i][1]);
245  auto iter = uniqueEdges.find(key);
246  if (iter == uniqueEdges.end())
247  {
248  std::vector<int> indices;
249  indices.push_back(i);
250  uniqueEdges.insert(std::make_pair(key, indices));
251  }
252  else
253  {
254  iter->second.push_back(i);
255  }
256  }
257  if (uniqueEdges.size() < edges.size())
258  {
259  // At least two edges are duplicated, possibly even a pair of
260  // edges (v0,v1) and (v1,v0) which is not allowed because the
261  // graph is undirected.
262  for (auto const& element : uniqueEdges)
263  {
264  if (element.second.size() > 1)
265  {
266  mDuplicatedEdges.push_back(element.second);
267  }
268  }
269  flags |= IPG_DUPLICATED_EDGES;
270  }
271 
272  // The edges are represented as pairs of indices into the 'positions'
273  // array. The indices for a single edge must be different (no edges
274  // allowed from a vertex to itself) and all indices must be valid.
275  // At the same time, keep track of unique edges.
276  for (int i = 0; i < numEdges; ++i)
277  {
278  std::array<int, 2> e = edges[i];
279  if (e[0] == e[1])
280  {
281  // The edge is degenerate, originating and terminating at the
282  // same vertex.
283  mDegenerateEdges.push_back(i);
284  flags |= IPG_DEGENERATE_EDGES;
285  }
286 
287  if (e[0] < 0 || e[0] >= numPositions || e[1] < 0 || e[1] >= numPositions)
288  {
289  // The edge has an index that references a non-existant vertex.
290  mEdgesWithInvalidVertices.push_back(i);
292  }
293  }
294 
295  return flags;
296 }
297 
298 template <typename Real>
299 void IsPlanarGraph<Real>::ComputeOverlappingRectangles(std::vector<std::array<Real, 2>> const& positions,
300  std::vector<std::array<int, 2>> const& edges, std::set<OrderedEdge>& overlappingRectangles) const
301 {
302  // Compute axis-aligned bounding rectangles for the edges.
303  int const numEdges = static_cast<int>(edges.size());
304  std::vector<std::array<Real, 2>> emin(numEdges);
305  std::vector<std::array<Real, 2>> emax(numEdges);
306  for (int i = 0; i < numEdges; ++i)
307  {
308  std::array<int, 2> e = edges[i];
309  std::array<Real, 2> const& p0 = positions[e[0]];
310  std::array<Real, 2> const& p1 = positions[e[1]];
311 
312  for (int j = 0; j < 2; ++j)
313  {
314  if (p0[j] < p1[j])
315  {
316  emin[i][j] = p0[j];
317  emax[i][j] = p1[j];
318  }
319  else
320  {
321  emin[i][j] = p1[j];
322  emax[i][j] = p0[j];
323  }
324  }
325  }
326 
327  // Get the rectangle endpoints.
328  int const numEndpoints = 2 * numEdges;
329  std::vector<Endpoint> xEndpoints(numEndpoints);
330  std::vector<Endpoint> yEndpoints(numEndpoints);
331  for (int i = 0, j = 0; i < numEdges; ++i)
332  {
333  xEndpoints[j].type = 0;
334  xEndpoints[j].value = emin[i][0];
335  xEndpoints[j].index = i;
336  yEndpoints[j].type = 0;
337  yEndpoints[j].value = emin[i][1];
338  yEndpoints[j].index = i;
339  ++j;
340 
341  xEndpoints[j].type = 1;
342  xEndpoints[j].value = emax[i][0];
343  xEndpoints[j].index = i;
344  yEndpoints[j].type = 1;
345  yEndpoints[j].value = emax[i][1];
346  yEndpoints[j].index = i;
347  ++j;
348  }
349 
350  // Sort the rectangle endpoints.
351  std::sort(xEndpoints.begin(), xEndpoints.end());
352  std::sort(yEndpoints.begin(), yEndpoints.end());
353 
354  // Sweep through the endpoints to determine overlapping x-intervals. Use
355  // an active set of rectangles to reduce the complexity of the algorithm.
356  std::set<int> active;
357  for (int i = 0; i < numEndpoints; ++i)
358  {
359  Endpoint const& endpoint = xEndpoints[i];
360  int index = endpoint.index;
361  if (endpoint.type == 0) // an interval 'begin' value
362  {
363  // In the 1D problem, the current interval overlaps with all the
364  // active intervals. In 2D this we also need to check for
365  // y-overlap.
366  for (auto activeIndex : active)
367  {
368  // Rectangles activeIndex and index overlap in the
369  // x-dimension. Test for overlap in the y-dimension.
370  std::array<Real, 2> const& r0min = emin[activeIndex];
371  std::array<Real, 2> const& r0max = emax[activeIndex];
372  std::array<Real, 2> const& r1min = emin[index];
373  std::array<Real, 2> const& r1max = emax[index];
374  if (r0max[1] >= r1min[1] && r0min[1] <= r1max[1])
375  {
376  if (activeIndex < index)
377  {
378  overlappingRectangles.insert(OrderedEdge(activeIndex, index));
379  }
380  else
381  {
382  overlappingRectangles.insert(OrderedEdge(index, activeIndex));
383  }
384  }
385  }
386  active.insert(index);
387  }
388  else // an interval 'end' value
389  {
390  active.erase(index);
391  }
392  }
393 }
394 
395 template <typename Real>
397  std::array<Real, 2> const& p0, std::array<Real, 2> const& p1,
398  std::array<Real, 2> const& q0, std::array<Real, 2> const& q1) const
399 {
400  // We must solve the two linear equations
401  // p0 + t0 * (p1 - p0) = q0 + t1 * (q1 - q0)
402  // for the unknown variables t0 and t1. These may be written as
403  // t0 * (p1 - p0) - t1 * (q1 - q0) = q0 - p0
404  // If denom = Dot(p1 - p0, Perp(q1 - q0)) is not zero, then
405  // t0 = Dot(q0 - p0, Perp(q1 - q0)) / denom = numer0 / denom
406  // t1 = Dot(q0 - p0, Perp(p1 - p0)) / denom = numer1 / denom
407  // For an invalid intersection, we need (t0,t1) with:
408  // ((0 < t0 < 1) and (0 <= t1 <= 1)) or ((0 <= t0 <= 1) and (0 < t1 < 1).
409  // If denom is zero, then
410 
411  std::array<Real, 2> p1mp0, q1mq0, q0mp0;
412  for (int j = 0; j < 2; ++j)
413  {
414  p1mp0[j] = p1[j] - p0[j];
415  q1mq0[j] = q1[j] - q0[j];
416  q0mp0[j] = q0[j] - p0[j];
417  }
418 
419  Real denom = p1mp0[0] * q1mq0[1] - p1mp0[1] * q1mq0[0];
420  Real numer0 = q0mp0[0] * q1mq0[1] - q0mp0[1] * q1mq0[0];
421  Real numer1 = q0mp0[0] * p1mp0[1] - q0mp0[1] * p1mp0[0];
422 
423  if (denom != mZero)
424  {
425  // The lines of the segments are not parallel.
426  if (denom > mZero)
427  {
428  if (mZero <= numer0 && numer0 <= denom && mZero <= numer1 && numer1 <= denom)
429  {
430  // The segments intersect.
431  return (numer0 != mZero && numer0 != denom) || (numer1 != mZero && numer1 != denom);
432  }
433  else
434  {
435  return false;
436  }
437  }
438  else // denom < mZero
439  {
440  if (mZero >= numer0 && numer0 >= denom && mZero >= numer1 && numer1 >= denom)
441  {
442  // The segments intersect.
443  return (numer0 != mZero && numer0 != denom) || (numer1 != mZero && numer1 != denom);
444  }
445  else
446  {
447  return false;
448  }
449  }
450  }
451  else
452  {
453  // The lines of the segments are parallel.
454  if (numer0 != mZero || numer1 != mZero)
455  {
456  // The lines of the segments are separated.
457  return false;
458  }
459  else
460  {
461  // The segments lie on the same line. Compute the parameter
462  // intervals for the segments in terms of the t0-parameter and
463  // determine their overlap (if any).
464  std::array<Real, 2> q1mp0;
465  for (int j = 0; j < 2; ++j)
466  {
467  q1mp0[j] = q1[j] - p0[j];
468  }
469  Real sqrLenP1mP0 = p1mp0[0] * p1mp0[0] + p1mp0[1] * p1mp0[1];
470  Real value0 = q0mp0[0] * p1mp0[0] + q0mp0[1] * p1mp0[1];
471  Real value1 = q1mp0[0] * p1mp0[0] + q1mp0[1] * p1mp0[1];
472  if ((value0 >= sqrLenP1mP0 && value1 >= sqrLenP1mP0)
473  || (value0 <= mZero && value1 <= mZero))
474  {
475  // If the segments intersect, they must do so at endpoints of
476  // the segments.
477  return false;
478  }
479  else
480  {
481  // The segments overlap in a positive-length interval.
482  return true;
483  }
484  }
485  }
486 }
487 
488 template <typename Real>
490 {
491  if (value < endpoint.value)
492  {
493  return true;
494  }
495  if (value > endpoint.value)
496  {
497  return false;
498  }
499  return type < endpoint.type;
500 }
501 
502 template <typename Real>
504 {
505  if (v0 < v1)
506  {
507  // v0 is minimum
508  V[0] = v0;
509  V[1] = v1;
510  }
511  else
512  {
513  // v1 is minimum
514  V[0] = v1;
515  V[1] = v0;
516  }
517 }
518 
519 template <typename Real>
521 {
522  // Lexicographical ordering used by std::array<int,2>.
523  return V < edge.V;
524 }
525 
526 }
std::vector< std::vector< int > > const & GetDuplicatedPositions() const
std::vector< int > const & GetEdgesWithInvalidVertices() const
OrderedEdge(int v0=-1, int v1=-1)
int IsValidTopology(std::vector< std::array< Real, 2 >> const &positions, std::vector< std::array< int, 2 >> const &edges)
bool InvalidSegmentIntersection(std::array< Real, 2 > const &p0, std::array< Real, 2 > const &p1, std::array< Real, 2 > const &q0, std::array< Real, 2 > const &q1) const
std::vector< OrderedEdge > const & GetInvalidIntersections() const
GLfloat GLfloat v1
Definition: glcorearb.h:812
GLsizei const GLfloat * value
Definition: glcorearb.h:819
bool operator<(Endpoint const &endpoint) const
std::vector< std::vector< int > > mDuplicatedEdges
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
std::vector< std::vector< int > > mDuplicatedPositions
bool operator<(OrderedEdge const &edge) const
GLbitfield flags
Definition: glcorearb.h:1591
std::vector< int > const & GetDegenerateEdges() const
GLfloat v0
Definition: glcorearb.h:811
std::vector< int > mEdgesWithInvalidVertices
std::vector< int > mDegenerateEdges
std::vector< OrderedEdge > mInvalidIntersections
GLuint index
Definition: glcorearb.h:781
std::vector< std::vector< int > > const & GetDuplicatedEdges() const
int operator()(std::vector< std::array< Real, 2 >> const &positions, std::vector< std::array< int, 2 >> const &edges)
GLfloat GLfloat p
Definition: glext.h:11668
void ComputeOverlappingRectangles(std::vector< std::array< Real, 2 >> const &positions, std::vector< std::array< int, 2 >> const &edges, std::set< OrderedEdge > &overlappingRectangles) const
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:103


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:00