vgl_convex_hull_2d.hxx
Go to the documentation of this file.
1 #ifndef vgl_convex_hull_2d_hxx_
2 #define vgl_convex_hull_2d_hxx_
3 #include <cstdlib>
4 #include <limits>
5 #include "vgl_convex_hull_2d.h"
6 #include <vgl/vgl_box_2d.h>
7 #include <vgl/vgl_area.h>
8 
9 #ifdef _MSC_VER
10 # include <vcl_msvc_warnings.h>
11 #endif
12 
13 #include <vnl/vnl_math.h>
14 
15 //:
16 // \file
17 // \brief two-dimensional convex hull
18 // read points from stdin,
19 // one point per line, as two numbers separated by whitespace
20 // on stdout, points on convex hull in order around hull, given
21 // by their numbers in input order
22 // the results should be "robust", and not return a wildly wrong hull,
23 // despite using floating point
24 // works in O(n log n); I think a bit faster than Graham scan;
25 // somewhat like Procedure 8.2 in Edelsbrunner's "Algorithms in Combinatorial
26 // Geometry", and very close to:
27 // A.M. Andrew, "Another Efficient Algorithm for Convex Hulls in Two Dimensions",
28 // Info. Proc. Letters 9, 216-219 (1979)
29 // (See also http://geometryalgorithms.com/Archive/algorithm_0110/algorithm_0110.htm)
30 //
31 // Ken Clarkson wrote this. Copyright (c) 1996 by AT&T..
32 // Permission to use, copy, modify, and distribute this software for any
33 // purpose without fee is hereby granted, provided that this entire notice
34 // is included in all copies of any software which is or includes a copy
35 // or modification of this software and in all copies of the supporting
36 // documentation for such software.
37 // THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
38 // WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
39 // REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
40 // OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
41 
42 #if 0
43 static void print_hull(double **P, int m)
44 {
45  for (int i=0; i<m; i++)
46  std::cout << (P[i]-points[0])/2) << ' ';
47  std::cout << std::endl;
48 }
49 #endif // 0
50 
51 static int ccw(double **P, int i, int j, int k)
52 {
53  double a = P[i][0] - P[j][0],
54  b = P[i][1] - P[j][1],
55  c = P[k][0] - P[j][0],
56  d = P[k][1] - P[j][1];
57  return a*d - b*c <= 0; // true if points i, j, k counterclockwise
58 }
59 
60 
61 #define CMPM(c,A,B) \
62  v = (*(double*const*)(A))[c] - (*(double*const*)(B))[c];\
63  if (v>0) return 1;\
64  if (v<0) return -1
65 
66 static int cmpl(const void *a, const void *b)
67 {
68  double v;
69  CMPM(0,a,b);
70  CMPM(1,b,a);
71  return 0;
72 }
73 #undef CMPM
74 
75 static int cmph(const void *a, const void *b) {return cmpl(b,a);}
76 
77 
78 static int make_chain(double** V, int n, int (*cmp)(const void*, const void*))
79 {
80  std::qsort(V, n, sizeof(double*), cmp);
81  int s = 1;
82  for (int i=2; i<n; i++) {
83  while (s>=1 && ccw(V, i, s, s-1)) --s;
84  ++s;
85  double* t = V[s]; V[s] = V[i]; V[i] = t;
86  }
87  return s;
88 }
89 
90 static inline int ch2d(double **P, int n)
91 {
92  int u = make_chain(P, n, cmpl); // make lower hull
93  if (!n) return 0;
94  P[n] = P[0];
95  return u+make_chain(P+u, n-u+1, cmph); // make upper hull
96 }
97 
98 template <class T>
100 vgl_convex_hull_2d (std::vector<vgl_point_2d<T> > const& points)
101 {
102  hull_valid_ = false;
103  points_ = points;
104 }
105 
106 template <class T>
108 {
109  //convert points to internal data structure
110  int N = points_.size();
111  if (N < 1)
112  return;
113 
114  double * array = new double[2*N];
115  double** points = new double*[N];
116  double** P = new double*[N+1];
117  for (int i = 0; i<N; i++)
118  points[i]=&array[2*i];
119 
120  for (int n = 0; n<N; n++)
121  {
122  points[n][0]=(double)points_[n].x();
123  points[n][1]=(double)points_[n].y();
124  P[n] = &points[n][0];
125  }
126  //the main hull routine
127  int n_hull = ch2d(P, N);
128 
129  //convert back to vgl_points
130  std::vector<vgl_point_2d<T> > temp;
131  for (int i = 0; i<n_hull; i++)
132  {
133  vgl_point_2d<T> p((T)P[i][0], (T)P[i][1]);
134  temp.push_back(p);
135  }
136  // Do not add last point if it is identical to the first one - PVr
137  if (P[0][0] != P[n_hull][0] || P[0][1] != P[n_hull][1])
138  temp.push_back(vgl_point_2d<T>((T)P[n_hull][0], (T)P[n_hull][1]));
139 
140  //construct the hull polygon
141  hull_ = vgl_polygon<T>(temp);
142  //clean up memory
143  delete [] array;
144  delete [] points;
145  delete [] P;
146  hull_valid_ = true;
147 }
148 
149 template <class T>
151 {
152  if (!hull_valid_)
153  this->compute_hull();
154  return hull_;
155 }
156 template <class T>
158  if (!hull_valid_)
159  this->compute_hull();
160  // the algorithm uses the fact that the smallest enclosing rectangle
161  // has a side in common with and edge of the convex hull
162  std::vector<vgl_point_2d<T> > verts = hull_[0];
163  size_t n = verts.size();
164  vgl_box_2d<T> min_box;
165  T min_angle = T(0);
166  T min_area = std::numeric_limits<T>::max();
167  vgl_vector_2d<T> min_offset(T(0),T(0));
168  for(size_t i = 1; i<=n; ++i){
169  vgl_point_2d<T>& vm1 = verts[i-1];
170  vgl_vector_2d<T> dir = verts[i%n]-vm1;
171  T theta = atan2(dir.y(), dir.x());
172  T c = cos(-theta), s=sin(-theta);
173  vgl_box_2d<T> rbox;
174  // rotate the vertices about v_i-1
175  // the resulting box needs to be shifted by v_i-1
176  for(size_t j = 0; j<n; ++j){
177  vgl_vector_2d<T> vp = verts[j]-vm1;
178  vgl_point_2d<T> rpp((c*vp.x()-s*vp.y()),(s*vp.x() + c*vp.y()));
179  rbox.add(rpp);
180  }
181  T area = vgl_area(rbox);
182  if(area<min_area){
183  min_offset.set(vm1.x(), vm1.y());
184  min_area = vgl_area(rbox);
185  min_box = rbox;
186  min_angle = theta;
187  }
188  }
189  T w = min_box.width(), h = min_box.height();
190  vgl_point_2d<T> c = min_box.centroid();
191  //select major axis such that width > height
192  T width = w, height = h;
193  vgl_point_2d<T> pmaj0(c.x()-width/T(2), c.y()), pmaj1(c.x()+width/T(2), c.y());
194  if(w<h){
195  width = h;
196  height = w;
197  pmaj0.set(c.x(), c.y()-width/T(2)); pmaj1.set(c.x(), c.y()+width/T(2));
198  }
199 
200  // rotate major axis about v_i-1
201  T cs = cos(min_angle), s = sin(min_angle);
202  T pmaj0x = pmaj0.x(), pmaj0y = pmaj0.y();
203  T pmaj1x = pmaj1.x(), pmaj1y = pmaj1.y();
204  vgl_point_2d<T> pmaj0r(cs*pmaj0x - s*pmaj0y, s*pmaj0x + cs*pmaj0y);
205  vgl_point_2d<T> pmaj1r(cs*pmaj1x - s*pmaj1y, s*pmaj1x + cs*pmaj1y);
206 
207  // add back rotation center
208  pmaj0r += min_offset; pmaj1r += min_offset;
209  return vgl_oriented_box_2d<T>(pmaj0r, pmaj1r, height);
210 }
211 //----------------------------------------------------------------------------
212 #undef VGL_CONVEX_HULL_2D_INSTANTIATE
213 #define VGL_CONVEX_HULL_2D_INSTANTIATE(T) \
214 /* template std::ostream& operator<<(std::ostream& s, vgl_convex_hull_2d<T >const& h); */ \
215 /* template std::istream& operator>>(std::istream& s, vgl_convex_hull_2d<T >& h); */ \
216 template class vgl_convex_hull_2d<T >
217 
218 #endif // vgl_convex_hull_2d_hxx_
Direction vector in Euclidean 2D space, templated by type of element.
Definition: vgl_fwd.h:12
T y() const
Definition: vgl_vector_2d.h:36
Compute the convex hull of a 2-d point set.
vgl_convex_hull_2d()=delete
void set(T vx, T vy)
Assignment.
Definition: vgl_vector_2d.h:55
Type width() const
Get width of this box (= x dimension).
Definition: vgl_box_2d.hxx:128
#define v
Definition: vgl_vector_2d.h:74
#define CMPM(c, A, B)
#define vp(os, v, s)
vgl_oriented_box_2d< T > min_area_enclosing_rectangle()
the oriented box enclosing the hull with minimum area.
vgl_polygon< T > hull()
void set(Type px, Type py)
Set x and y.
Definition: vgl_point_2d.h:79
Type & y()
Definition: vgl_point_2d.h:72
Contains class to represent a cartesian 2D bounding box.
vgl_point_2d< Type > centroid() const
Get the centroid point.
Definition: vgl_box_2d.hxx:154
Type height() const
Get height of this box (= y dimension).
Definition: vgl_box_2d.hxx:134
void add(vgl_point_2d< Type > const &p)
Add a point to this box.
Definition: vgl_box_2d.hxx:317
T x() const
Definition: vgl_vector_2d.h:35
Store a polygon.
Definition: vgl_area.h:6
T vgl_area(vgl_polygon< T > const &poly)
The area of a polygon.
Type & x()
Definition: vgl_point_2d.h:71