GteMinimumAreaCircle2.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 <LowLevel/GteLogger.h>
13 #include <algorithm>
14 #include <random>
15 
16 // Compute the minimum area circle containing the input set of points. The
17 // algorithm randomly permutes the input points so that the construction
18 // occurs in 'expected' O(N) time. All internal minimal circle calculations
19 // store the squared radius in the radius member of Circle2. Only at the
20 // end is a sqrt computed.
21 
22 namespace gte
23 {
24 
25 template <typename InputType, typename ComputeType>
27 {
28 public:
29  bool operator()(int numPoints, Vector2<InputType> const* points,
30  Circle2<InputType>& minimal);
31 
32  // Member access.
33  inline int GetNumSupport() const;
34  inline std::array<int, 3> const& GetSupport() const;
35 
36 private:
37  // Test whether point P is inside circle C using squared distance and
38  // squared radius.
39  bool Contains(int i, Circle2<ComputeType> const& circle) const;
40 
41  Circle2<ComputeType> ExactCircle1(int i0) const;
42  Circle2<ComputeType> ExactCircle2(int i0, int i1) const;
43  Circle2<ComputeType> ExactCircle3(int i0, int i1, int i2) const;
44 
48 
49  // Indices of points that support current minimum area circle.
50  bool SupportContains(int j) const;
51 
53  std::array<int, 3> mSupport;
54 
55  // Random permutation of the unique input points to produce expected
56  // linear time for the algorithm.
57  std::vector<Vector2<ComputeType>> mComputePoints;
58 };
59 
60 
61 template <typename InputType, typename ComputeType>
64 {
65  if (numPoints >= 1 && points)
66  {
67  // Function array to avoid switch statement in the main loop.
68  std::function<Circle2<ComputeType>(int)> update[4];
69  update[1] = [this](int i) { return UpdateSupport1(i); };
70  update[2] = [this](int i) { return UpdateSupport2(i); };
71  update[3] = [this](int i) { return UpdateSupport3(i); };
72 
73  // Process only the unique points.
74  std::vector<int> permuted(numPoints);
75  for (int i = 0; i < numPoints; ++i)
76  {
77  permuted[i] = i;
78  }
79  std::sort(permuted.begin(), permuted.end(),
80  [points](int i0, int i1) { return points[i0] < points[i1]; });
81  auto end = std::unique(permuted.begin(), permuted.end(),
82  [points](int i0, int i1) { return points[i0] == points[i1]; });
83  permuted.erase(end, permuted.end());
84  numPoints = static_cast<int>(permuted.size());
85 
86  // Create a random permutation of the points.
87  std::shuffle(permuted.begin(), permuted.end(),
88  std::default_random_engine());
89 
90  // Convert to the compute type, which is a simple copy when
91  // ComputeType is the same as InputType.
92  mComputePoints.resize(numPoints);
93  for (int i = 0; i < numPoints; ++i)
94  {
95  for (int j = 0; j < 2; ++j)
96  {
97  mComputePoints[i][j] = points[permuted[i]][j];
98  }
99  }
100 
101  // Start with the first point.
102  Circle2<ComputeType> ctMinimal = ExactCircle1(0);
103  mNumSupport = 1;
104  mSupport[0] = 0;
105 
106  // The loop restarts from the beginning of the point list each time
107  // the circle needs updating. Linus Källberg (Computer Science at
108  // Mälardalen University in Sweden) discovered that performance is
109  // better when the remaining points in the array are processed before
110  // restarting. The points processed before the point that caused the
111  // update are likely to be enclosed by the new circle (or near the
112  // circle boundary) because they were enclosed by the previous circle.
113  // The chances are better that points after the current one will cause
114  // growth of the bounding circle.
115  for (int i = 1 % numPoints, n = 0; i != n; i = (i + 1) % numPoints)
116  {
117  if (!SupportContains(i))
118  {
119  if (!Contains(i, ctMinimal))
120  {
121  Circle2<ComputeType> circle = update[mNumSupport](i);
122  if (circle.radius > ctMinimal.radius)
123  {
124  ctMinimal = circle;
125  n = i;
126  }
127  }
128  }
129  }
130 
131  for (int j = 0; j < 2; ++j)
132  {
133  minimal.center[j] = static_cast<InputType>(ctMinimal.center[j]);
134  }
135  minimal.radius = static_cast<InputType>(ctMinimal.radius);
136  minimal.radius = sqrt(minimal.radius);
137 
138  for (int i = 0; i < mNumSupport; ++i)
139  {
140  mSupport[i] = permuted[mSupport[i]];
141  }
142  return true;
143  }
144  else
145  {
146  LogError("Input must contain points.");
147  minimal.center = Vector2<InputType>::Zero();
148  minimal.radius = std::numeric_limits<InputType>::max();
149  return false;
150  }
151 }
152 
153 template <typename InputType, typename ComputeType> inline
155 {
156  return mNumSupport;
157 }
158 
159 template <typename InputType, typename ComputeType> inline
160 std::array<int, 3> const&
162 {
163  return mSupport;
164 }
165 
166 template <typename InputType, typename ComputeType>
168  Circle2<ComputeType> const& circle) const
169 {
170  // NOTE: In this algorithm, circle.radius is the *squared radius*.
171  Vector2<ComputeType> diff = mComputePoints[i] - circle.center;
172  return Dot(diff, diff) <= circle.radius;
173 }
174 
175 template <typename InputType, typename ComputeType>
177 ExactCircle1(int i0) const
178 {
179  Circle2<ComputeType> minimal;
180  minimal.center = mComputePoints[i0];
181  minimal.radius = (ComputeType)0;
182  return minimal;
183 }
184 
185 template <typename InputType, typename ComputeType>
187 ExactCircle2(int i0, int i1) const
188 {
189  Vector2<ComputeType> const& P0 = mComputePoints[i0];
190  Vector2<ComputeType> const& P1 = mComputePoints[i1];
191  Vector2<ComputeType> diff = P1 - P0;
192  Circle2<ComputeType> minimal;
193  minimal.center = ((ComputeType)0.5)*(P0 + P1);
194  minimal.radius = ((ComputeType)0.25)*Dot(diff, diff);
195  return minimal;
196 }
197 
198 template <typename InputType, typename ComputeType>
200 ExactCircle3(int i0, int i1, int i2) const
201 {
202  // Compute the 2D circle containing P0, P1, and P2. The center in
203  // barycentric coordinates is C = x0*P0 + x1*P1 + x2*P2, where
204  // x0 + x1 + x2 = 1. The center is equidistant from the three points,
205  // so |C - P0| = |C - P1| = |C - P2| = R, where R is the radius of the
206  // circle. From these conditions,
207  // C - P0 = x0*E0 + x1*E1 - E0
208  // C - P1 = x0*E0 + x1*E1 - E1
209  // C - P2 = x0*E0 + x1*E1
210  // where E0 = P0 - P2 and E1 = P1 - P2, which leads to
211  // r^2 = |x0*E0 + x1*E1|^2 - 2*Dot(E0, x0*E0 + x1*E1) + |E0|^2
212  // r^2 = |x0*E0 + x1*E1|^2 - 2*Dot(E1, x0*E0 + x1*E1) + |E1|^2
213  // r^2 = |x0*E0 + x1*E1|^2
214  // Subtracting the last equation from the first two and writing the
215  // equations as a linear system,
216  //
217  // +- -++ -+ +- -+
218  // | Dot(E0,E0) Dot(E0,E1) || x0 | = 0.5 | Dot(E0,E0) |
219  // | Dot(E1,E0) Dot(E1,E1) || x1 | | Dot(E1,E1) |
220  // +- -++ -+ +- -+
221  //
222  // The following code solves this system for x0 and x1 and then evaluates
223  // the third equation in r^2 to obtain r.
224 
225  Vector2<ComputeType> const& P0 = mComputePoints[i0];
226  Vector2<ComputeType> const& P1 = mComputePoints[i1];
227  Vector2<ComputeType> const& P2 = mComputePoints[i2];
228 
229  Vector2<ComputeType> E0 = P0 - P2;
230  Vector2<ComputeType> E1 = P1 - P2;
231 
233  A(0, 0) = Dot(E0, E0);
234  A(0, 1) = Dot(E0, E1);
235  A(1, 0) = A(0, 1);
236  A(1, 1) = Dot(E1, E1);
237 
238  ComputeType const half = (ComputeType)0.5;
239  Vector2<ComputeType> B{ half * A(0, 0), half* A(1, 1) };
240 
241  Circle2<ComputeType> minimal;
244  {
245  ComputeType x2 = (ComputeType)1 - X[0] - X[1];
246  minimal.center = X[0] * P0 + X[1] * P1 + x2 *P2;
247  Vector2<ComputeType> tmp = X[0] * E0 + X[1] * E1;
248  minimal.radius = Dot(tmp, tmp);
249  }
250  else
251  {
253  minimal.radius = (ComputeType)std::numeric_limits<InputType>::max();
254  }
255 
256  return minimal;
257 }
258 
259 template <typename InputType, typename ComputeType>
262 {
263  Circle2<ComputeType> minimal = ExactCircle2(mSupport[0], i);
264  mNumSupport = 2;
265  mSupport[1] = i;
266  return minimal;
267 }
268 
269 template <typename InputType, typename ComputeType>
272 {
273  // Permutations of type 2, used for calling ExactCircle2(...).
274  int const numType2 = 2;
275  int const type2[numType2][2] =
276  {
277  { 0, /*2*/ 1 },
278  { 1, /*2*/ 0 }
279  };
280 
281  // Permutations of type 3, used for calling ExactCircle3(...).
282  int const numType3 = 1; // {0, 1, 2}
283 
284  Circle2<ComputeType> circle[numType2 + numType3];
285  ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
286  int iCircle = 0, iMinRSqr = -1;
287  int k0, k1;
288 
289  // Permutations of type 2.
290  for (int j = 0; j < numType2; ++j, ++iCircle)
291  {
292  k0 = mSupport[type2[j][0]];
293  circle[iCircle] = ExactCircle2(k0, i);
294  if (circle[iCircle].radius < minRSqr)
295  {
296  k1 = mSupport[type2[j][1]];
297  if (Contains(k1, circle[iCircle]))
298  {
299  minRSqr = circle[iCircle].radius;
300  iMinRSqr = iCircle;
301  }
302  }
303  }
304 
305  // Permutations of type 3.
306  k0 = mSupport[0];
307  k1 = mSupport[1];
308  circle[iCircle] = ExactCircle3(k0, k1, i);
309  if (circle[iCircle].radius < minRSqr)
310  {
311  minRSqr = circle[iCircle].radius;
312  iMinRSqr = iCircle;
313  }
314 
315  // For exact arithmetic, iMinRSqr >= 0, but for floating-point arithmetic,
316  // round-off errors can lead to iMinRSqr == -1. When this happens, just
317  // use the circle for the current support.
318  if (iMinRSqr == -1)
319  {
320  iMinRSqr = iCircle;
321  }
322 
323  switch (iMinRSqr)
324  {
325  case 0:
326  mSupport[1] = i;
327  break;
328  case 1:
329  mSupport[0] = i;
330  break;
331  case 2:
332  mNumSupport = 3;
333  mSupport[2] = i;
334  break;
335  }
336 
337  return circle[iMinRSqr];
338 }
339 
340 template <typename InputType, typename ComputeType>
343 {
344  // Permutations of type 2, used for calling ExactCircle2(...).
345  int const numType2 = 3;
346  int const type2[numType2][3] =
347  {
348  { 0, /*3*/ 1, 2 },
349  { 1, /*3*/ 0, 2 },
350  { 2, /*3*/ 0, 1 }
351  };
352 
353  // Permutations of type 2, used for calling ExactCircle3(...).
354  int const numType3 = 3;
355  int const type3[numType3][3] =
356  {
357  { 0, 1, /*3*/ 2 },
358  { 0, 2, /*3*/ 1 },
359  { 1, 2, /*3*/ 0 }
360  };
361 
362  Circle2<ComputeType> circle[numType2 + numType3];
363  ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
364  int iCircle = 0, iMinRSqr = -1;
365  int k0, k1, k2;
366 
367  // Permutations of type 2.
368  for (int j = 0; j < numType2; ++j, ++iCircle)
369  {
370  k0 = mSupport[type2[j][0]];
371  circle[iCircle] = ExactCircle2(k0, i);
372  if (circle[iCircle].radius < minRSqr)
373  {
374  k1 = mSupport[type2[j][1]];
375  k2 = mSupport[type2[j][2]];
376  if (Contains(k1, circle[iCircle])
377  && Contains(k2, circle[iCircle]))
378  {
379  minRSqr = circle[iCircle].radius;
380  iMinRSqr = iCircle;
381  }
382  }
383  }
384 
385  // Permutations of type 3.
386  for (int j = 0; j < numType3; ++j, ++iCircle)
387  {
388  k0 = mSupport[type3[j][0]];
389  k1 = mSupport[type3[j][1]];
390  circle[iCircle] = ExactCircle3(k0, k1, i);
391  if (circle[iCircle].radius < minRSqr)
392  {
393  k2 = mSupport[type3[j][2]];
394  if (Contains(k2, circle[iCircle]))
395  {
396  minRSqr = circle[iCircle].radius;
397  iMinRSqr = iCircle;
398  }
399  }
400  }
401 
402  // For exact arithmetic, iMinRSqr >= 0, but for floating-point arithmetic,
403  // round-off errors can lead to iMinRSqr == -1. When this happens, just
404  // use the circle for the current support.
405  if (iMinRSqr == -1)
406  {
407  iMinRSqr = iCircle;
408  }
409 
410  switch (iMinRSqr)
411  {
412  case 0:
413  mNumSupport = 2;
414  mSupport[1] = i;
415  break;
416  case 1:
417  mNumSupport = 2;
418  mSupport[0] = i;
419  break;
420  case 2:
421  mNumSupport = 2;
422  mSupport[0] = mSupport[2];
423  mSupport[1] = i;
424  break;
425  case 3:
426  mSupport[2] = i;
427  break;
428  case 4:
429  mSupport[1] = i;
430  break;
431  case 5:
432  mSupport[0] = i;
433  break;
434  }
435 
436  return circle[iMinRSqr];
437 }
438 
439 template <typename InputType, typename ComputeType>
441 {
442  for (int i = 0; i < mNumSupport; ++i)
443  {
444  if (j == mSupport[i])
445  {
446  return true;
447  }
448  }
449  return false;
450 }
451 
452 
453 }
GLdouble n
Definition: glcorearb.h:2003
Circle2< ComputeType > UpdateSupport1(int i)
Circle2< ComputeType > UpdateSupport2(int i)
std::array< int, 3 > const & GetSupport() const
Circle2< ComputeType > ExactCircle2(int i0, int i1) const
Circle2< ComputeType > ExactCircle3(int i0, int i1, int i2) const
bool Contains(int i, Circle2< ComputeType > const &circle) const
bool SupportContains(int j) const
GLfixed GLfixed GLint GLint GLfixed points
Definition: glext.h:4927
GLuint GLuint end
Definition: glcorearb.h:470
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
#define LogError(message)
Definition: GteLogger.h:92
static Vector Zero()
Circle2< ComputeType > ExactCircle1(int i0) const
std::array< int, 3 > mSupport
DualQuaternion< Real > Dot(DualQuaternion< Real > const &d0, DualQuaternion< Real > const &d1)
std::vector< Vector2< ComputeType > > mComputePoints
Circle2< ComputeType > UpdateSupport3(int i)
bool operator()(int numPoints, Vector2< InputType > const *points, Circle2< InputType > &minimal)
GLfixed GLfixed x2
Definition: glext.h:4952
Vector< N, Real > center


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