vgl_clip.hxx
Go to the documentation of this file.
1 // This is core/vgl/vgl_clip.hxx
2 #ifndef vgl_clip_hxx_
3 #define vgl_clip_hxx_
4 //:
5 // \file
6 // \author fsm
7 
8 #include <cstdlib>
9 #include <cstdio>
10 #include <algorithm>
11 #include <limits>
12 #include <cmath>
13 #include "vgl_clip.h"
14 #ifdef _MSC_VER
15 # include <vcl_msvc_warnings.h>
16 #endif
17 
18 template <class T>
19 bool vgl_clip_lineseg_to_line(T &x1, T &y1,
20  T &x2, T &y2,
21  T a,T b,T c)
22 {
23  T f1 = a*x1+b*y1+c;
24  T f2 = a*x2+b*y2+c;
25  if (f1<0) {
26  if (f2<0) // both out
27  return false;
28  // 1 out, 2 in
29  x1 = (f2*x1 - f1*x2)/(f2-f1);
30  y1 = (f2*y1 - f1*y2)/(f2-f1);
31  return true;
32  }
33  else {
34  if (f2<0) // 1 in, 2 out
35  {
36  x2 = (f2*x1 - f1*x2)/(f2-f1);
37  y2 = (f2*y1 - f1*y2)/(f2-f1);
38  }
39  // both in
40  return true;
41  }
42 }
43 
44 template <class T>
45 bool vgl_clip_line_to_box(T a, T b, T c, // line coefficients.
46  T x1, T y1, // bounding
47  T x2, T y2, // box.
48  T &bx, T &by, // start and
49  T &ex, T &ey) // end points.
50 {
51  if (x1>x2) std::swap(x1,x2);
52  if (y1>y2) std::swap(y1,y2);
53  // now x1 <= x2 and y1 <= y2
54 
55  if (a == 0 && b == 0) return false; // then ax+by+c=0 is the line at infinity
56 
57  bool b_set = false, // has the point (bx,by) been set to a valid point?
58  e_set = false; // has the point (ex,ey) been set to a valid point?
59 
60  if (a != 0) // line is not horizontal
61  {
62  // Intersection point with the line y=y1:
63  by = y1; bx = -(b*y1+c)/a;
64  // Intersection point with the line y=y2:
65  ey = y2; ex = -(b*y2+c)/a;
66 
67  b_set = bx >= x1 && bx <= x2; // does this intersection point
68  e_set = ex >= x1 && ex <= x2; // lie on the bounding box?
69  }
70 
71  if (b_set && e_set) return true;
72  if (b_set) { std::swap(bx,ex); std::swap(by,ey); std::swap(b_set,e_set); }
73  // now b_set is false
74 
75  if (b != 0) // line is not vertical
76  {
77  // Intersection point with the line x=x1:
78  bx = x1; by = -(a*x1+c)/b;
79  b_set = by >= y1 && by <= y2;
80  if (b_set && e_set) return true;
81  if (b_set) { std::swap(bx,ex); std::swap(by,ey); e_set=true; }
82 
83  // Intersection point with the line x=x2:
84  bx = x2; by = -(a*x2+c)/b;
85  b_set = by >= y1 && ey <= y2;
86  }
87 
88  return b_set && e_set;
89 }
90 
91 
92 // This license is very restrictive, prefer Angus Johnson's more liberal Clipper library.
93 #ifdef BUILD_NONCOMMERCIAL
94 
95 extern "C" {
96 #include "internals/gpc.h"
97 }
98 
99 #define MALLOC(p, T, c, s) { if ((c) > 0) { \
100  p= (T*)std::malloc(c * sizeof(T)); if (!(p)) { \
101  std::fprintf(stderr, "vgl: gpc malloc failure: %s\n", s); \
102  std::exit(0);}} else p=NULL; }
103 
104 #define FREE(p) { if (p) { std::free(p); (p)= NULL; } }
105 
106 namespace {
107  //: Creates a gpc polygon from a vgl_polygon.
108  // The caller is responsible for freeing the gpc_polygon.
109  template <class T>
111  vgl_to_gpc( const vgl_polygon<T>& vgl_poly )
112  {
113  gpc_polygon gpc_poly;
114  gpc_poly.num_contours = vgl_poly.num_sheets();
115  MALLOC( gpc_poly.hole, int, gpc_poly.num_contours, "allocating hole array" );
116  MALLOC( gpc_poly.contour, gpc_vertex_list, gpc_poly.num_contours, "allocating contour array" );
117  for ( int s = 0; s < gpc_poly.num_contours; ++s ) {
118  gpc_poly.hole[s] = 0;
119  gpc_poly.contour[s].num_vertices = vgl_poly[s].size();
120  MALLOC( gpc_poly.contour[s].vertex, gpc_vertex, vgl_poly[s].size(), "allocating vertex list" );
121  for ( unsigned int p = 0; p < vgl_poly[s].size(); ++p ) {
122  gpc_poly.contour[s].vertex[p].x = vgl_poly[s][p].x();
123  gpc_poly.contour[s].vertex[p].y = vgl_poly[s][p].y();
124  }
125  }
126 
127  return gpc_poly;
128  }
129 
130  //: Adds a gpc_polygon to a given vgl_polygon.
131  template <class T>
132  void
133  add_gpc_to_vgl( vgl_polygon<T>& vgl_poly, const gpc_polygon& gpc_poly )
134  {
135  for ( int c=0; c < gpc_poly.num_contours; ++c ) {
136  vgl_poly.new_sheet();
137  for ( int p=0; p < gpc_poly.contour[c].num_vertices; ++p ) {
138  vgl_poly.push_back( T(gpc_poly.contour[c].vertex[p].x),
139  T(gpc_poly.contour[c].vertex[p].y) );
140  }
141  }
142  }
143 }
144 
145 #elif HAS_CLIPPER
146 
147 #include <clipper.hxx>
148 
149 namespace {
150  //: Creates a Clipper polygon from a vgl_polygon.
151  template <class T>
152  ClipperLib::Paths
153  vgl_to_clipper( const vgl_polygon<T>& vgl_poly, double scale )
154  {
155  ClipperLib::Paths clipper_poly;
156  for ( size_t s = 0; s < vgl_poly.num_sheets(); ++s ) {
157  ClipperLib::Path path;
158  for ( size_t p = 0; p < vgl_poly[s].size(); ++p ) {
159  ClipperLib::IntPoint pt((ClipperLib::cInt)((double)vgl_poly[s][p].x()*scale),
160  (ClipperLib::cInt)((double)vgl_poly[s][p].y()*scale));
161  path.push_back(pt);
162  }
163  clipper_poly.push_back(path);
164  }
165 
166  return clipper_poly;
167  }
168 
169  //: Adds a Clipper polygon to a given vgl_polygon.
170  template <class T>
171  void
172  add_clipper_to_vgl( vgl_polygon<T>& vgl_poly, const ClipperLib::Paths& clipper_poly, double scale )
173  {
174  for (const auto & c : clipper_poly) {
175  vgl_poly.new_sheet();
176  for ( size_t p=0; p < c.size(); ++p ) {
177  vgl_poly.push_back( T((double)c[p].X/scale),
178  T((double)c[p].Y/scale) );
179  }
180  }
181  }
182 }
183 
184 template <class T>
185 void
186 bounds(vgl_polygon<T> vgl_poly, T& min_x, T& max_x, T& min_y, T& max_y)
187 {
188  for (size_t s=0; s < vgl_poly.num_sheets(); ++s) {
189  for (size_t p=0; p < vgl_poly[s].size(); ++p) {
190  if(s==0 && p==0) { // not the most ideal way to initilize this...
191  min_x = max_x = vgl_poly[0][0].x();
192  min_y = max_y = vgl_poly[0][0].y();
193  }
194 
195  min_x = std::min(vgl_poly[s][p].x(), min_x);
196  min_y = std::min(vgl_poly[s][p].y(), min_y);
197  max_x = std::max(vgl_poly[s][p].x(), max_x);
198  max_y = std::max(vgl_poly[s][p].y(), max_y);
199  }
200  }
201 }
202 
203 #endif
204 
205 template <class T>
207 vgl_clip(vgl_polygon<T> const& poly1, vgl_polygon<T> const& poly2, vgl_clip_type op, int *p_retval)
208 {
209  // Check for the null case
210  if ( poly1.num_sheets() == 0 ) {
211  *p_retval = 1;
212  switch ( op )
213  {
214  case vgl_clip_type_intersect: return poly1;
215  case vgl_clip_type_difference: return poly1;
216  case vgl_clip_type_union: return poly2;
217  case vgl_clip_type_xor: return poly2;
218  default: *p_retval = -1; return vgl_polygon<T>(); // this should not happen...
219  }
220  }
221  if ( poly2.num_sheets() == 0 ) {
222  *p_retval = 1;
223  switch ( op )
224  {
225  case vgl_clip_type_intersect: return poly2;
226  case vgl_clip_type_difference: return poly1;
227  case vgl_clip_type_union: return poly1;
228  case vgl_clip_type_xor: return poly1;
229  default: *p_retval = -1; return vgl_polygon<T>(); // this should not happen...
230  }
231  }
232 
233  vgl_polygon<T> result;
234 
235 #ifdef BUILD_NONCOMMERCIAL
236  gpc_polygon p1 = vgl_to_gpc( poly1 );
237  gpc_polygon p2 = vgl_to_gpc( poly2 );
238  gpc_polygon p3;
239 
240  gpc_op g_op = GPC_INT;
241  switch ( op )
242  {
243  case vgl_clip_type_intersect: g_op = GPC_INT; break;
244  case vgl_clip_type_difference: g_op = GPC_DIFF; break;
245  case vgl_clip_type_union: g_op = GPC_UNION; break;
246  case vgl_clip_type_xor: g_op = GPC_XOR; break;
247  default: break;
248  }
249 
250  int retval = gpc_polygon_clip( g_op, &p1, &p2, &p3 );
251  *p_retval = retval;
252 
253  if (retval == 0) {
254  gpc_free_polygon( &p1 );
255  gpc_free_polygon( &p2 );
256  return result;
257  }
258  add_gpc_to_vgl( result, p3 );
259 
260  gpc_free_polygon( &p1 );
261  gpc_free_polygon( &p2 );
262  gpc_free_polygon( &p3 );
263 
264 #elif HAS_CLIPPER
265  ClipperLib::Clipper clpr;
266  // Clipper operates in fixed point space (because it is more robust), so we need
267  // to compute a scale factor to preserve precision.
268  // per Angus Johnson, "if any coordinate value exceeds +/-3.0e+9, large integer
269  // math slows clipping by about 10%"
270  int halfSignificantDigits = std::numeric_limits<ClipperLib::cInt>::digits10/2;
271 
272  T min_x, max_x, min_y, max_y;
273  bounds( poly1, min_x, max_x, min_y, max_y);
274  max_x = std::max(max_x, std::abs(min_x));
275  max_y = std::max(max_y, std::abs(min_y));
276  T max1 = std::max(max_x, max_y);
277 
278  bounds( poly2, min_x, max_x, min_y, max_y);
279  max_x = std::max(max_x, std::abs(min_x));
280  max_y = std::max(max_y, std::abs(min_y));
281  T max2 = std::max(max_x, max_y);
282 
283  T max = std::max(max1, max2);
284  double scale = std::pow(10.0, halfSignificantDigits) / max;
285 
286 
287  ClipperLib::Paths p1 = vgl_to_clipper( poly1, scale );
288  ClipperLib::Paths p2 = vgl_to_clipper( poly2, scale );
289  ClipperLib::Paths p3;
290 
291  ClipperLib::ClipType g_op = ClipperLib::ctIntersection;
292  switch ( op )
293  {
294  case vgl_clip_type_intersect: g_op = ClipperLib::ctIntersection; break;
295  case vgl_clip_type_difference: g_op = ClipperLib::ctDifference; break;
296  case vgl_clip_type_union: g_op = ClipperLib::ctUnion; break;
297  case vgl_clip_type_xor: g_op = ClipperLib::ctXor; break;
298  default: break;
299  }
300 
301 
302  clpr.AddPaths(p1, ClipperLib::ptSubject, true);
303  clpr.AddPaths(p2, ClipperLib::ptClip, true);
304  int retval = clpr.Execute(g_op, p3, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd);
305  *p_retval = retval;
306 
307  add_clipper_to_vgl( result, p3, scale );
308 
309 #else
310  *p_retval = -1;
311  std::fprintf(stdout,"WARNING: GPC is only free for non-commercial use -- assuming disjoint polygons.\n");
312  std::fprintf(stderr,"WARNING: GPC is only free for non-commercial use -- assuming disjoint polygons.\n");
313  switch ( op )
314  {
315  default:
316  case vgl_clip_type_intersect: result = vgl_polygon<T>(); break; // empty
317  case vgl_clip_type_difference: result = poly1; break;
318  case vgl_clip_type_union:
319  case vgl_clip_type_xor:
320  result = poly1;
321  for (unsigned int i=0; i<poly2.num_sheets(); ++i)
322  result.push_back(poly2[i]);
323  break;
324  }
325 
326 #endif
327 
328  return result;
329 }
330 
331 template <class T>
333 vgl_clip(vgl_polygon<T> const& poly1, vgl_polygon<T> const& poly2, vgl_clip_type op )
334 {
335  int retval;
336  return vgl_clip(poly1, poly2, op, &retval);
337 }
338 
339 #undef VGL_CLIP_INSTANTIATE
340 #define VGL_CLIP_INSTANTIATE(T) \
341 template vgl_polygon<T > vgl_clip(vgl_polygon<T >const&,vgl_polygon<T >const&,vgl_clip_type); \
342 template vgl_polygon<T > vgl_clip(vgl_polygon<T >const&,vgl_polygon<T >const&,vgl_clip_type,int *); \
343 template bool vgl_clip_lineseg_to_line(T&,T&,T&,T&,T,T,T); \
344 template bool vgl_clip_line_to_box(T,T,T,T,T,T,T,T&,T&,T&,T&); \
345 template vgl_line_segment_2d<T > vgl_clip_line_to_box(vgl_line_2d<T >const&,vgl_box_2d<T >const&)
346 
347 #endif // vgl_clip_hxx_
double y
Definition: gpc.h:73
gpc_op
Definition: gpc.h:62
bool vgl_clip_lineseg_to_line(T &x1, T &y1, T &x2, T &y2, T a, T b, T c)
clips away the portion where ax+by+c<0. return false if nothing left.
Definition: vgl_clip.hxx:19
Definition: gpc.h:64
gpc_vertex_list * contour
Definition: gpc.h:86
Definition: gpc.h:65
void new_sheet()
Add a new (empty) sheet to the polygon.
Definition: vgl_polygon.h:105
vgl_polygon< T > vgl_clip(const vgl_polygon< T > &poly1, const vgl_polygon< T > &poly2, vgl_clip_type op=vgl_clip_type_intersect)
Clip a polygon against another polygon.
Definition: gpc.h:66
gpc_vertex * vertex
Definition: gpc.h:79
void push_back(T x, T y)
Add a new point to the last sheet.
Definition: vgl_polygon.hxx:81
int num_contours
Definition: gpc.h:84
int * hole
Definition: gpc.h:85
Definition: gpc.h:70
vgl_clip_type
Type of polygon "clip" operations.
Definition: vgl_clip.h:23
int num_vertices
Definition: gpc.h:78
vgl_line_segment_2d< T > vgl_clip_line_to_box(vgl_line_2d< T > const &l, vgl_box_2d< T > const &b)
clip given line to given box, and return resulting line segment.
Definition: vgl_clip.h:55
Definition: gpc.h:67
void gpc_free_polygon(gpc_polygon *polygon)
int gpc_polygon_clip(gpc_op set_operation, gpc_polygon *subject_polygon, gpc_polygon *clip_polygon, gpc_polygon *result_polygon)
unsigned int num_sheets() const
Definition: vgl_polygon.h:116
Store a polygon.
Definition: vgl_area.h:6
double x
Definition: gpc.h:72