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