GteConvexHull2.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 // Compute the convex hull of 2D points using a divide-and-conquer algorithm.
11 // This is an O(N log N) algorithm for N input points. The only way to ensure
12 // a correct result for the input vertices (assumed to be exact) is to choose
13 // ComputeType for exact rational arithmetic. You may use BSNumber. No
14 // divisions are performed in this computation, so you do not have to use
15 // BSRational.
16 //
17 // The worst-case choices of N for Real of type BSNumber or BSRational with
18 // integer storage UIntegerFP32<N> are listed in the next table. The numerical
19 // computations are encapsulated in PrimalQuery2<Real>::ToLineExtended. We
20 // recommend using only BSNumber, because no divisions are performed in the
21 // convex-hull computations.
22 //
23 // input type | compute type | N
24 // -----------+--------------+------
25 // float | BSNumber | 18
26 // double | BSNumber | 132
27 // float | BSRational | 214
28 // double | BSRational | 1587
29 
30 #include <LowLevel/GteLogger.h>
32 #include <Mathematics/GteLine.h>
33 #include <algorithm>
34 #include <vector>
35 
36 namespace gte
37 {
38 
39 template <typename InputType, typename ComputeType>
41 {
42 public:
43  // The class is a functor to support computing the convex hull of multiple
44  // data sets using the same class object.
45  ConvexHull2();
46 
47  // The input is the array of points whose convex hull is required. The
48  // epsilon value is used to determine the intrinsic dimensionality of the
49  // vertices (d = 0, 1, or 2). When epsilon is positive, the determination
50  // is fuzzy--points approximately the same point, approximately on a
51  // line, or planar. The return value is 'true' if and only if the hull
52  // construction is successful.
53  bool operator()(int numPoints, Vector2<InputType> const* points, InputType epsilon);
54 
55  // Dimensional information. If GetDimension() returns 1, the points lie
56  // on a line P+t*D (fuzzy comparison when epsilon > 0). You can sort
57  // these if you need a polyline output by projecting onto the line each
58  // vertex X = P+t*D, where t = Dot(D,X-P).
59  inline InputType GetEpsilon() const;
60  inline int GetDimension() const;
61  inline Line2<InputType> const& GetLine() const;
62 
63  // Member access.
64  inline int GetNumPoints() const;
65  inline int GetNumUniquePoints() const;
66  inline Vector2<InputType> const* GetPoints() const;
67  inline PrimalQuery2<ComputeType> const& GetQuery() const;
68 
69  // The convex hull is a convex polygon whose vertices are listed in
70  // counterclockwise order.
71  inline std::vector<int> const& GetHull() const;
72 
73 private:
74  // Support for divide-and-conquer.
75  void GetHull(int& i0, int& i1);
76  void Merge(int j0, int j1, int j2, int j3, int& i0, int& i1);
77  void GetTangent(int j0, int j1, int j2, int j3, int& i0, int& i1);
78 
79  // The epsilon value is used for fuzzy determination of intrinsic
80  // dimensionality. If the dimension is 0 or 1, the constructor returns
81  // early. The caller is responsible for retrieving the dimension and
82  // taking an alternate path should the dimension be smaller than 2. If
83  // the dimension is 0, the caller may as well treat all points[] as a
84  // single point, say, points[0]. If the dimension is 1, the caller can
85  // query for the approximating line and project points[] onto it for
86  // further processing.
87  InputType mEpsilon;
90 
91  // The array of points used for geometric queries. If you want to be
92  // certain of a correct result, choose ComputeType to be BSNumber.
93  std::vector<Vector2<ComputeType>> mComputePoints;
95 
99  std::vector<int> mMerged, mHull;
100 };
101 
102 
103 template <typename InputType, typename ComputeType>
105  :
106  mEpsilon((InputType)0),
107  mDimension(0),
108  mLine(Vector2<InputType>::Zero(), Vector2<InputType>::Zero()),
109  mNumPoints(0),
110  mNumUniquePoints(0),
111  mPoints(nullptr)
112 {
113 }
114 
115 template <typename InputType, typename ComputeType>
117  Vector2<InputType> const* points, InputType epsilon)
118 {
119  mEpsilon = std::max(epsilon, (InputType)0);
120  mDimension = 0;
121  mLine.origin = Vector2<InputType>::Zero();
122  mLine.direction = Vector2<InputType>::Zero();
123  mNumPoints = numPoints;
124  mNumUniquePoints = 0;
125  mPoints = points;
126  mMerged.clear();
127  mHull.clear();
128 
129  int i, j;
130  if (mNumPoints < 3)
131  {
132  // ConvexHull2 should be called with at least three points.
133  return false;
134  }
135 
137  if (info.dimension == 0)
138  {
139  // mDimension is 0
140  return false;
141  }
142 
143  if (info.dimension == 1)
144  {
145  // The set is (nearly) collinear.
146  mDimension = 1;
147  mLine = Line2<InputType>(info.origin, info.direction[0]);
148  return false;
149  }
150 
151  mDimension = 2;
152 
153  // Compute the points for the queries.
154  mComputePoints.resize(mNumPoints);
156  for (i = 0; i < mNumPoints; ++i)
157  {
158  for (j = 0; j < 2; ++j)
159  {
160  mComputePoints[i][j] = points[i][j];
161  }
162  }
163 
164  // Sort the points.
165  mHull.resize(mNumPoints);
166  for (i = 0; i < mNumPoints; ++i)
167  {
168  mHull[i] = i;
169  }
170  std::sort(mHull.begin(), mHull.end(),
171  [points](int i0, int i1)
172  {
173  if (points[i0][0] < points[i1][0]) { return true; }
174  if (points[i0][0] > points[i1][0]) { return false; }
175  return points[i0][1] < points[i1][1];
176  }
177  );
178 
179  // Remove duplicates.
180  auto newEnd = std::unique(mHull.begin(), mHull.end(),
181  [points](int i0, int i1)
182  {
183  return points[i0] == points[i1];
184  }
185  );
186  mHull.erase(newEnd, mHull.end());
187  mNumUniquePoints = static_cast<int>(mHull.size());
188 
189  // Use a divide-and-conquer algorithm. The merge step computes the
190  // convex hull of two convex polygons.
191  mMerged.resize(mNumUniquePoints);
192  int i0 = 0, i1 = mNumUniquePoints - 1;
193  GetHull(i0, i1);
194  mHull.resize(i1 - i0 + 1);
195  return true;
196 }
197 
198 template <typename InputType, typename ComputeType> inline
200 {
201  return mEpsilon;
202 }
203 
204 template <typename InputType, typename ComputeType> inline
206 {
207  return mDimension;
208 }
209 
210 template <typename InputType, typename ComputeType> inline
212 {
213  return mLine;
214 }
215 
216 template <typename InputType, typename ComputeType> inline
218 {
219  return mNumPoints;
220 }
221 
222 template <typename InputType, typename ComputeType> inline
224 {
225  return mNumUniquePoints;
226 }
227 
228 template <typename InputType, typename ComputeType> inline
230 {
231  return mPoints;
232 }
233 
234 template <typename InputType, typename ComputeType> inline
236 {
237  return mQuery;
238 }
239 
240 template <typename InputType, typename ComputeType> inline
241 std::vector<int> const& ConvexHull2<InputType, ComputeType>::GetHull() const
242 {
243  return mHull;
244 }
245 
246 template <typename InputType, typename ComputeType> inline
248 {
249  int numVertices = i1 - i0 + 1;
250  if (numVertices > 1)
251  {
252  // Compute the middle index of input range.
253  int mid = (i0 + i1) / 2;
254 
255  // Compute the hull of subsets (mid-i0+1 >= i1-mid).
256  int j0 = i0, j1 = mid, j2 = mid + 1, j3 = i1;
257  GetHull(j0, j1);
258  GetHull(j2, j3);
259 
260  // Merge the convex hulls into a single convex hull.
261  Merge(j0, j1, j2, j3, i0, i1);
262  }
263  // else: The convex hull is a single point.
264 }
265 
266 template <typename InputType, typename ComputeType> inline
267 void ConvexHull2<InputType, ComputeType>::Merge(int j0, int j1, int j2, int j3,
268  int& i0, int& i1)
269 {
270  // Subhull0 is to the left of subhull1 because of the initial sorting of
271  // the points by x-components. We need to find two mutually visible
272  // points, one on the left subhull and one on the right subhull.
273  int size0 = j1 - j0 + 1;
274  int size1 = j3 - j2 + 1;
275 
276  int i;
278 
279  // Find the right-most point of the left subhull.
281  int imax0 = j0;
282  for (i = j0 + 1; i <= j1; ++i)
283  {
284  p = mComputePoints[mHull[i]];
285  if (pmax0 < p)
286  {
287  pmax0 = p;
288  imax0 = i;
289  }
290  }
291 
292  // Find the left-most point of the right subhull.
293  Vector2<ComputeType> pmin1 = mComputePoints[mHull[j2]];
294  int imin1 = j2;
295  for (i = j2 + 1; i <= j3; ++i)
296  {
297  p = mComputePoints[mHull[i]];
298  if (p < pmin1)
299  {
300  pmin1 = p;
301  imin1 = i;
302  }
303  }
304 
305  // Get the lower tangent to hulls (LL = lower-left, LR = lower-right).
306  int iLL = imax0, iLR = imin1;
307  GetTangent(j0, j1, j2, j3, iLL, iLR);
308 
309  // Get the upper tangent to hulls (UL = upper-left, UR = upper-right).
310  int iUL = imax0, iUR = imin1;
311  GetTangent(j2, j3, j0, j1, iUR, iUL);
312 
313  // Construct the counterclockwise-ordered merged-hull vertices.
314  int k;
315  int numMerged = 0;
316 
317  i = iUL;
318  for (k = 0; k < size0; ++k)
319  {
320  mMerged[numMerged++] = mHull[i];
321  if (i == iLL)
322  {
323  break;
324  }
325  i = (i < j1 ? i + 1 : j0);
326  }
327  LogAssert(k < size0, "Unexpected condition.");
328 
329  i = iLR;
330  for (k = 0; k < size1; ++k)
331  {
332  mMerged[numMerged++] = mHull[i];
333  if (i == iUR)
334  {
335  break;
336  }
337  i = (i < j3 ? i + 1 : j2);
338  }
339  LogAssert(k < size1, "Unexpected condition.");
340 
341  int next = j0;
342  for (k = 0; k < numMerged; ++k)
343  {
344  mHull[next] = mMerged[k];
345  ++next;
346  }
347 
348  i0 = j0;
349  i1 = next - 1;
350 }
351 
352 template <typename InputType, typename ComputeType> inline
353 void ConvexHull2<InputType, ComputeType>::GetTangent(int j0, int j1, int j2, int j3,
354  int& i0, int& i1)
355 {
356  // In theory the loop terminates in a finite number of steps, but the
357  // upper bound for the loop variable is used to trap problems caused by
358  // floating point roundoff errors that might lead to an infinite loop.
359 
360  int size0 = j1 - j0 + 1;
361  int size1 = j3 - j2 + 1;
362  int const imax = size0 + size1;
363  int i, iLm1, iRp1;
364  Vector2<ComputeType> L0, L1, R0, R1;
365 
366  for (i = 0; i < imax; i++)
367  {
368  // Get the endpoints of the potential tangent.
369  L1 = mComputePoints[mHull[i0]];
370  R0 = mComputePoints[mHull[i1]];
371 
372  // Walk along the left hull to find the point of tangency.
373  if (size0 > 1)
374  {
375  iLm1 = (i0 > j0 ? i0 - 1 : j1);
376  L0 = mComputePoints[mHull[iLm1]];
377  auto order = mQuery.ToLineExtended(R0, L0, L1);
380  {
381  i0 = iLm1;
382  continue;
383  }
384  }
385 
386  // Walk along right hull to find the point of tangency.
387  if (size1 > 1)
388  {
389  iRp1 = (i1 < j3 ? i1 + 1 : j2);
390  R1 = mComputePoints[mHull[iRp1]];
391  auto order = mQuery.ToLineExtended(L1, R0, R1);
394  {
395  i1 = iRp1;
396  continue;
397  }
398  }
399 
400  // The tangent segment has been found.
401  break;
402  }
403 
404  // Detect an "infinite loop" caused by floating point round-off errors.
405  LogAssert(i < imax, "Unexpected condition.");
406 }
407 
408 
409 }
Vector2< Real > origin
Definition: GteVector2.h:85
GLfixed GLfixed GLint GLint order
Definition: glext.h:4927
std::vector< int > const & GetHull() const
OrderType ToLineExtended(Vector2< Real > const &P, Vector2< Real > const &Q0, Vector2< Real > const &Q1) const
PrimalQuery2< ComputeType > mQuery
void Set(int numVertices, Vector2< Real > const *vertices)
Vector2< InputType > const * mPoints
#define LogAssert(condition, message)
Definition: GteLogger.h:86
Line2< InputType > mLine
bool operator()(int numPoints, Vector2< InputType > const *points, InputType epsilon)
int GetNumPoints() const
PrimalQuery2< ComputeType > const & GetQuery() const
Line2< InputType > const & GetLine() const
int GetNumUniquePoints() const
void Merge(int j0, int j1, int j2, int j3, int &i0, int &i1)
std::vector< Vector2< ComputeType > > mComputePoints
void GetTangent(int j0, int j1, int j2, int j3, int &i0, int &i1)
GLfixed GLfixed GLint GLint GLfixed points
Definition: glext.h:4927
int GetDimension() const
static Vector Zero()
InputType mEpsilon
std::vector< int > mHull
InputType GetEpsilon() const
std::vector< int > mMerged
GLfloat GLfloat p
Definition: glext.h:11668
Vector2< InputType > const * GetPoints() const
Vector2< Real > direction[2]
Definition: GteVector2.h:86


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:59