vgl_polygon.hxx
Go to the documentation of this file.
1 // This is core/vgl/vgl_polygon.hxx
2 #ifndef vgl_polygon_hxx_
3 #define vgl_polygon_hxx_
4 
5 #include <iostream>
6 #include <set>
7 #include <cmath>
8 #include <algorithm>
9 #include <numeric>
10 #include <string>
11 #include "vgl_polygon.h"
12 #include <vnl/vnl_math.h>
13 //:
14 // \file
15 
16 #include "vgl_intersection.h"
17 #include "vgl_line_2d.h"
18 #include "vgl_tolerance.h"
19 
20 #ifdef _MSC_VER
21 # include <vcl_msvc_warnings.h>
22 #endif
23 #include <cassert>
24 
25 // Constructors/Destructor---------------------------------------------------
26 
27 template <class T>
29  sheets_(1, sheet_t(n))
30 {
31  for (int i = 0; i < n; ++i)
32  sheets_[0][i].set(p[i].x(), p[i].y());
33 }
34 
35 template <class T>
36 vgl_polygon<T>::vgl_polygon(T const* x, T const* y, int n)
37  : sheets_(1, sheet_t(n))
38 {
39  for (int i = 0; i < n; ++i)
40  sheets_[0][i].set(x[i], y[i]);
41 }
42 
43 template <class T>
44 vgl_polygon<T>::vgl_polygon(T const x_y[], int n)
45  : sheets_(1, sheet_t(n))
46 {
47  for (int i = 0; i < n; ++i)
48  sheets_[0][i].set(x_y[2*i], x_y[2*i+1]);
49 }
50 
51 template <class T>
52 void vgl_polygon<T>::add_contour(point_t const p[], int n)
53 {
54  sheet_t s(n);
55  for (int i = 0; i < n; ++i)
56  s[i].set(p[i].x(), p[i].y());
57  sheets_.push_back(s);
58 }
59 
60 template <class T>
61 void vgl_polygon<T>::add_contour(T const* x, T const* y, int n)
62 {
63  sheet_t s(n);
64  for (int i = 0; i < n; ++i)
65  s[i].set(x[i], y[i]);
66  sheets_.push_back(s);
67 }
68 
69 template <class T>
70 void vgl_polygon<T>::add_contour(T const x_y[], int n)
71 {
72  sheet_t s(n);
73  for (int i = 0; i < n; ++i)
74  s[i].set(x_y[2*i], x_y[2*i+1]);
75  sheets_.push_back(s);
76 }
77 
78 // Functions -----------------------------------------------------------------
79 
80 template <class T>
82 {
83  sheets_[sheets_.size()-1].push_back(point_t(x,y));
84 }
85 
86 template <class T>
88 {
89  sheets_[sheets_.size()-1].push_back(p);
90 }
91 
92 template <class T>
93 bool vgl_polygon<T>::contains(T x, T y) const
94 {
95  bool c = false;
96  for (unsigned int s=0; s < sheets_.size(); ++s)
97  {
98  sheet_t const& pgon = sheets_[s];
99  int n = int(pgon.size());
100  for (int i = 0, j = n-1; i < n; j = i++)
101  {
102  const vgl_point_2d<T> &p_i = pgon[i];
103  const vgl_point_2d<T> &p_j = pgon[j];
104 
105  // by definition, corner points and edge points are inside the polygon:
106  if ((p_j.x() - x) * (p_i.y() - y) == (p_i.x() - x) * (p_j.y() - y) &&
107  (((p_i.x()<=x) && (x<=p_j.x())) || ((p_j.x()<=x) && (x<=p_i.x()))) &&
108  (((p_i.y()<=y) && (y<=p_j.y())) || ((p_j.y()<=y) && (y<=p_i.y()))))
109  return true;
110  // invert c for each edge crossing:
111  if ((((p_i.y()<=y) && (y<p_j.y())) || ((p_j.y()<=y) && (y<p_i.y()))) &&
112  (x < (p_j.x() - p_i.x()) * (y - p_i.y()) / (p_j.y() - p_i.y()) + p_i.x()))
113  c = !c;
114  }
115  }
116  return c;
117 }
118 
119 template <class T>
120 std::ostream& vgl_polygon<T>::print(std::ostream& os) const
121 {
122  if (sheets_.size() == 0)
123  os << "Empty polygon\n";
124  else {
125  os << "Polygon with " << num_sheets() << " sheets:\n";
126  for (unsigned int s = 0; s < sheets_.size(); ++s)
127  {
128  os << "Sheet " << s << ' ';
129  if (sheets_[s].size()==0)
130  os << "(empty)";
131  else{
132  os << " nverts = " << sheets_[s].size() << '\n';
133  for (unsigned int p = 0; p < sheets_[s].size(); ++p)
134  os << "( " << sheets_[s][p].x() << " , " << sheets_[s][p].y() << " ) ";
135  os << '\n';
136  }
137  }
138  }
139  return os;
140 }
141 template <class T>
142 std::istream& vgl_polygon<T>::read(std::istream& is){
143  std::string s;
144  is >> s;
145  if(s == "Empty polygon")
146  return is;
147  is >> s; // skip "with"
148  unsigned n_sheets;
149  is >> n_sheets;
150  if(n_sheets == 0)
151  return is;
152  is >> s; // Skip "sheets:"
153  unsigned k;
154  sheets_.resize(n_sheets);
155  for(unsigned sh = 0; sh<n_sheets; ++sh){
156  is >> s; // Sheet
157  is >> k;
158  is >> s; // nverts or empty
159  if(s == "(empty)")
160  return is;
161  is >> s;// =
162  unsigned nv;
163  is >> nv;
164  for(unsigned iv = 0; iv<nv; ++iv){
165  T x, y;
166  is >> s; //'('
167  is >> x ;
168  is >> s; //','
169  is >> y;
170  is >> s; //')'
171  vgl_point_2d<T> pt(x, y);
172  sheets_[sh].push_back(pt);
173  }
174  }
175  return is;
176 }
177 
178 template <class T>
180 {
181  assert(p.num_sheets()==1);
182  n = int(p[0].size());
183  x = new T[n*2];
184  y = x + n;
185  for (int v = 0; v < n; ++v)
186  {
187  x[v] = p[0][v].x();
188  y[v] = p[0][v].y();
189  }
190 }
191 
192 template <class T>
194 {
195  n = int(p.size());
196  x = new T[n*2];
197  y = x + n;
198  for (int v = 0; v < n; ++v)
199  {
200  x[v] = p[v].x();
201  y[v] = p[v].y();
202  }
203 }
204 
205 template <class T>
207 {
208  delete [] x;
209  // do not delete [] y; only one alloc in ctor.
210 }
211 
212 //: Compute all self-intersections between all edges on all sheets.
213 // \returns three arrays \a e1, \a e2, and \a ip of equal size.
214 // Corresponding elements from these arrays describe an intersection.
215 // e1[k].first is the sheet index containing edge (e1[k].second, e1[k].second+1)
216 // involved in the k-th intersection. Similarly, e2[k] indexes the other
217 // edge involved in the k-th intersection. The corresponding intersection
218 // point is returned in ip[k].
219 template <class T>
221  std::vector<std::pair<unsigned int,unsigned int> >& e1,
222  std::vector<std::pair<unsigned int,unsigned int> >& e2,
223  std::vector<vgl_point_2d<T> >& ip)
224 {
225  const T tol = std::sqrt(vgl_tolerance<T>::position);
226  std::vector<unsigned int> offsets(p.num_sheets()+1,0);
227  for (unsigned int s = 0; s < p.num_sheets(); ++s) {
228  offsets[s+1] = offsets[s]+(unsigned int)(p[s].size());
229  }
230  e1.clear();
231  e2.clear();
232  ip.clear();
233 
234  typedef std::pair<unsigned int, unsigned int> upair;
235  std::set<upair> isect_set;
236  for (unsigned int s1 = 0; s1 < p.num_sheets(); ++s1)
237  {
238  const typename vgl_polygon<T>::sheet_t& sheet1 = p[s1];
239  if (sheet1.size() < 2)
240  continue;
241  for (unsigned int i1=(unsigned int)(sheet1.size()-1), i1n=0; i1n < sheet1.size(); i1=i1n, ++i1n)
242  {
243  const vgl_point_2d<T>& v1 = sheet1[i1];
244  const vgl_point_2d<T>& v2 = sheet1[i1n];
245  // coefficients for linear equation for testing intersections
246  // for (x,y) if cx*x+cy*y+c changes sign then we have intersection
247  T cx = v1.y()-v2.y();
248  T cy = v2.x()-v1.x();
249  T c = v1.x()*v2.y()-v2.x()*v1.y();
250  T norm = 1/std::sqrt(cx*cx+cy*cy);
251  cx *= norm;
252  cy *= norm;
253  c *= norm;
254 
255  unsigned int idx1 = offsets[s1]+i1;
256 
257  // inner loop - compare against self
258  for (unsigned int s2 = 0; s2 < p.num_sheets(); ++s2)
259  {
260  const typename vgl_polygon<T>::sheet_t& sheet2 = p[s2];
261  const unsigned int size2 = (unsigned int)(sheet2.size());
262  if (size2 < 2)
263  continue;
264  unsigned int start = 0, end = 0;
265  if (s1 == s2) {
266  start = (i1n+1)%size2;
267  end = (i1+size2-1)%size2;
268  }
269 
270  T dist = cx*sheet2[start].x()+cy*sheet2[start].y()+c;
271  bool last_sign = dist > 0;
272  bool last_zero = std::abs(dist) <= tol;
273  bool first = true;
274  for (unsigned int i2=start, i2n=(start+1)%size2; i2!=end || first; i2=i2n, i2n=(i2n+1)%size2)
275  {
276  const vgl_point_2d<T>& v3 = sheet2[i2];
277  const vgl_point_2d<T>& v4 = sheet2[i2n];
278  dist = cx*v4.x()+cy*v4.y()+c;
279  bool sign = dist > 0;
280  bool zero = std::abs(dist) <= tol;
281  if (sign != last_sign || zero || last_zero)
282  {
283  unsigned int idx2 = offsets[s2]+i2;
284  upair pair_idx(idx1,idx2);
285  if (pair_idx.first > pair_idx.second) {
286  pair_idx = upair(idx2,idx1);
287  }
288  std::set<upair>::iterator f = isect_set.find(pair_idx);
289  if (f == isect_set.end())
290  isect_set.insert(pair_idx);
291  // use vgl_intersection to verify some degenerate false positives
292  else if (vgl_intersection(v1,v2,v3,v4,tol)) {
293  // make intersection point
294  vgl_point_2d<T> ipt;
295  if (!vgl_intersection(vgl_line_2d<T>(v1,v2),vgl_line_2d<T>(v3,v4),ipt)
296  || parallel(v2-v1,v4-v3,tol)) // vgl_intersection test is not accurate enough
297  {
298  // use the median point when lines are parallel
299  vgl_vector_2d<T> dir = v2-v1;
300  normalize(dir);
301  T t1 = 0;
302  T t2 = dot_product(dir,v2-v1);
303  T t3 = dot_product(dir,v3-v1);
304  T t4 = dot_product(dir,v4-v1);
305  T t = t1+t2+t3+t4;
306  t -= std::min(std::min(t1,t2),std::min(t3,t4));
307  t -= std::max(std::max(t1,t2),std::max(t3,t4));
308  t /= 2;
309  ipt = v1 + t*dir;
310  }
311  e1.emplace_back(s2,i2);
312  e2.emplace_back(s1,i1);
313  ip.push_back(ipt);
314  }
315  }
316  last_sign = sign;
317  last_zero = zero;
318  first = false;
319  }
320  }
321  }
322  }
323 }
324 
325 //turn all sheets into counterclockwise polygons
326 template <class T>
328 {
329  vgl_polygon<T> outPolygon;
330  //std::vector<std::vector<vgl_point_2d<T> > > outPolySheets;
331  std::vector<std::pair<unsigned,unsigned> > e1, e2;
332  std::vector<vgl_point_2d<T> > ip;
333  vgl_selfintersections(p, e1, e2, ip);
334  if(!e1.empty() && !e2.empty() && !ip.empty())
335  {
336  //Self intersecting/non simple polygons cannot be said to be clockwise or not
337  //hence they are not added and we get an empty polygon.
338  std::cout<< "WARNING - self intersecting polygon"<<std::endl;
339  return p;
340  }
341  else
342  {
343  for(size_t i = 0; i < p.num_sheets();i++)
344  {
345  std::vector<vgl_point_2d<T> > verts = p[i];
347  {
348  outPolygon.push_back(verts);
349  }
350  else
351  {
352  if(i == 0)
353  {std::reverse(verts.begin(), verts.end());}
354  outPolygon.push_back(verts);
355  }
356  }
357  }
358  return outPolygon;
359 }
360 
361 template <class T>
363 {
364  double sum = 0.0;
365  vgl_point_2d<T> v1;
366  vgl_point_2d<T> v2;
367  double term1, term2;
368  for(size_t x = 1; x <verts.size(); x++)
369  {
370  v1 = verts.at(x-1);
371  v2 = verts.at(x);
372  term1 = double(v2.x())-double(v1.x());
373  term2 = double(v2.y())+double(v1.y());
374  sum += term1*term2;
375  }
376  v1 = verts.at(verts.size()-1);
377  v2 = verts.at(0);
378  term1 = double(v2.x())-double(v1.x());
379  term2 = double(v2.y())+double(v1.y());
380  sum += term1*term2;
381  return sum < 0.0;
382 
383 }
384 
385 
386 #undef VGL_POLYGON_INSTANTIATE
387 #define VGL_POLYGON_INSTANTIATE(T) \
388 template class vgl_polygon<T >; \
389 template struct vgl_polygon_sheet_as_array<T >; \
390 template std::ostream& operator<<(std::ostream&,vgl_polygon<T > const&); \
391 template std::istream& operator>>(std::istream&,vgl_polygon<T >&); \
392 template void vgl_selfintersections(vgl_polygon<T > const& p, \
393  std::vector<std::pair<unsigned int,unsigned int> >& e1, \
394  std::vector<std::pair<unsigned int,unsigned int> >& e2, \
395  std::vector<vgl_point_2d<T > >& ip); \
396 template vgl_polygon<T> vgl_reorient_polygon(vgl_polygon<T > const &p); \
397 template bool vgl_polygon_sheet_is_counter_clockwise(std::vector<vgl_point_2d<T > > verts)
398 
399 #endif // vgl_polygon_hxx_
vgl_polygon()=default
Default constructor - constructs an empty polygon with no sheets.
Set of intersection functions.
T dot_product(v const &a, v const &b)
dot product or inner product of two vectors.
bool contains(point_t const &p) const
Returns true if p(x,y) is inside the polygon, else false.
Definition: vgl_polygon.h:81
Direction vector in Euclidean 2D space, templated by type of element.
Definition: vgl_fwd.h:12
v & normalize(v &a)
Normalise by dividing through by the length, thus returning a length 1 vector.
std::ostream & print(std::ostream &) const
Pretty print.
vgl_polygon< T > vgl_reorient_polygon(vgl_polygon< T > const &p)
void add_contour(point_t const p[], int n)
Add a single sheet to this polygon, specified by the given list of n points.
Definition: vgl_polygon.hxx:52
bool vgl_polygon_sheet_is_counter_clockwise(std::vector< vgl_point_2d< T > > verts)
vgl_point_3d< T > vgl_intersection(const std::vector< vgl_plane_3d< T > > &p)
Return the intersection point of vector of planes.
#define v
Definition: vgl_vector_2d.h:74
void vgl_selfintersections(vgl_polygon< T > const &p, std::vector< std::pair< unsigned int, unsigned int > > &e1, std::vector< std::pair< unsigned int, unsigned int > > &e2, std::vector< vgl_point_2d< T > > &ip)
Compute all self-intersections between all edges on all sheets.
vgl_polygon_sheet_as_array(vgl_polygon< T > const &p)
Automatic constructor from a single-sheet polygon.
Type & y()
Definition: vgl_point_2d.h:72
void push_back(T x, T y)
Add a new point to the last sheet.
Definition: vgl_polygon.hxx:81
~vgl_polygon_sheet_as_array()
Destructor.
bool parallel(v const &a, v const &b, double eps=0.0)
are two vectors parallel, i.e., is one a scalar multiple of the other?.
std::vector< point_t > sheet_t
Definition: vgl_polygon.h:43
unsigned int num_sheets() const
Definition: vgl_polygon.h:116
Store a polygon.
Definition: vgl_area.h:6
std::istream & read(std::istream &)
read this polygon from ascii stream.
std::vector< sheet_t > sheets_
Definition: vgl_polygon.h:137
Type & x()
Definition: vgl_point_2d.h:71