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>
vgl_clip_lineseg_to_line(T & x1,T & y1,T & x2,T & y2,T a,T b,T c)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>
vgl_clip_line_to_box(T a,T b,T c,T x1,T y1,T x2,T y2,T & bx,T & by,T & ex,T & ey)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>
110   gpc_polygon
vgl_to_gpc(const vgl_polygon<T> & vgl_poly)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
add_gpc_to_vgl(vgl_polygon<T> & vgl_poly,const gpc_polygon & gpc_poly)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
vgl_to_clipper(const vgl_polygon<T> & vgl_poly,double scale)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
add_clipper_to_vgl(vgl_polygon<T> & vgl_poly,const ClipperLib::Paths & clipper_poly,double scale)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
bounds(vgl_polygon<T> vgl_poly,T & min_x,T & max_x,T & min_y,T & max_y)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>
206 vgl_polygon<T>
vgl_clip(vgl_polygon<T> const & poly1,vgl_polygon<T> const & poly2,vgl_clip_type op,int * p_retval)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>
332 vgl_polygon<T>
vgl_clip(vgl_polygon<T> const & poly1,vgl_polygon<T> const & poly2,vgl_clip_type op)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_
348