1 /*******************************************************************************
2 *                                                                              *
3 * Author    :  Angus Johnson                                                   *
4 * Version   :  1.1                                                             *
5 * Date      :  4 April 2011                                                    *
6 * Website   :  http://www.angusj.com                                           *
7 * Copyright :  Angus Johnson 2010-2011                                         *
8 *                                                                              *
9 * License:                                                                     *
10 * Use, modification & distribution is subject to Boost Software License Ver 1. *
11 * http://www.boost.org/LICENSE_1_0.txt                                         *
12 *                                                                              *
13 *******************************************************************************/
14 
15 #ifndef AGG_CONV_CLIPPER_INCLUDED
16 #define AGG_CONV_CLIPPER_INCLUDED
17 
18 #if defined(_MSC_VER) && (_MSC_VER >= 1800)
19 #include <algorithm>
20 #endif
21 
22 #include <cmath>
23 #include "agg_basics.h"
24 #include "agg_array.h"
25 #include "clipper.hpp"
26 
27 namespace mapserver
28 {
29   enum clipper_op_e { clipper_or,
30     clipper_and, clipper_xor, clipper_a_minus_b, clipper_b_minus_a };
31   enum clipper_PolyFillType {clipper_even_odd, clipper_non_zero, clipper_positive, clipper_negative};
32 
33   template<class VSA, class VSB> class conv_clipper
34   {
35     enum status { status_move_to, status_line_to, status_stop };
36     typedef VSA source_a_type;
37     typedef VSB source_b_type;
38     typedef conv_clipper<source_a_type, source_b_type> self_type;
39 
40   private:
41     source_a_type*							m_src_a;
42     source_b_type*							m_src_b;
43     status									m_status;
44     int										m_vertex;
45     int										m_contour;
46 	int										m_scaling_factor;
47     clipper_op_e							m_operation;
48     pod_bvector<ClipperLib::IntPoint, 8>		m_vertex_accumulator;
49     ClipperLib::Polygons						m_poly_a;
50     ClipperLib::Polygons						m_poly_b;
51     ClipperLib::Polygons						m_result;
52     ClipperLib::Clipper						m_clipper;
53     clipper_PolyFillType					m_subjFillType;
54     clipper_PolyFillType					m_clipFillType;
55 
Round(double val)56     int Round(double val)
57     {
58     if ((val < 0)) return (int)(val - 0.5); else return (int)(val + 0.5);
59     }
60 
61   public:
62     conv_clipper(source_a_type &a, source_b_type &b,
63       clipper_op_e op = clipper_or,
64       clipper_PolyFillType subjFillType = clipper_even_odd,
65       clipper_PolyFillType clipFillType = clipper_even_odd,
66 	  int scaling_factor = 2) :
67         m_src_a(&a),
68         m_src_b(&b),
69         m_status(status_move_to),
70         m_vertex(-1),
71         m_contour(-1),
72         m_operation(op),
73         m_subjFillType(subjFillType),
74         m_clipFillType(clipFillType)
75     {
76 		m_scaling_factor = std::max(std::min(scaling_factor, 6),0);
77 		m_scaling_factor = Round(std::pow((double)10, m_scaling_factor));
78 	}
79 
~conv_clipper()80     ~conv_clipper()
81     {
82     }
83 
84     void attach1(VSA &source, clipper_PolyFillType subjFillType = clipper_even_odd)
85       { m_src_a = &source; m_subjFillType = subjFillType; }
86     void attach2(VSB &source, clipper_PolyFillType clipFillType = clipper_even_odd)
87       { m_src_b = &source; m_clipFillType = clipFillType; }
88 
operation(clipper_op_e v)89     void operation(clipper_op_e v) { m_operation = v; }
90 
91     void rewind(unsigned path_id);
92     unsigned vertex(double* x, double* y);
93 
94     bool next_contour();
95     bool next_vertex(double* x, double* y);
96     void start_extracting();
97     void add_vertex_(double &x, double &y);
98     void end_contour(ClipperLib::Polygons &p);
99 
add(VS & src,ClipperLib::Polygons & p)100 	template<class VS> void add(VS &src, ClipperLib::Polygons &p){
101 		unsigned cmd;
102 		double x; double y; double start_x; double start_y;
103 		bool starting_first_line;
104 
105 		start_x = 0.0;
106 		start_y = 0.0;
107 		starting_first_line = true;
108 		p.resize(0);
109 
110 		cmd = src->vertex( &x , &y );
111 		while(!is_stop(cmd))
112 		{
113 		  if(is_vertex(cmd))
114 		  {
115 			if(is_move_to(cmd))
116 			{
117 			  if(!starting_first_line ) end_contour(p);
118 			  start_x = x;
119 			  start_y = y;
120 			}
121 			add_vertex_( x, y );
122 			starting_first_line = false;
123 		  }
124 		  else if(is_end_poly(cmd))
125 		  {
126 			if(!starting_first_line && is_closed(cmd))
127 			  add_vertex_( start_x, start_y );
128 		  }
129 		  cmd = src->vertex( &x, &y );
130 		}
131 		end_contour(p);
132 	}
133   };
134 
135   //------------------------------------------------------------------------
136 
137   template<class VSA, class VSB>
start_extracting()138   void conv_clipper<VSA, VSB>::start_extracting()
139   {
140     m_status = status_move_to;
141     m_contour = -1;
142     m_vertex = -1;
143   }
144   //------------------------------------------------------------------------------
145 
146   template<class VSA, class VSB>
rewind(unsigned path_id)147   void conv_clipper<VSA, VSB>::rewind(unsigned path_id)
148   {
149     m_src_a->rewind( path_id );
150     m_src_b->rewind( path_id );
151 
152     add( m_src_a , m_poly_a );
153     add( m_src_b , m_poly_b );
154     m_result.resize(0);
155 
156     ClipperLib::PolyFillType pftSubj, pftClip;
157     switch (m_subjFillType)
158     {
159       case clipper_even_odd: pftSubj = ClipperLib::pftEvenOdd; break;
160       case clipper_non_zero: pftSubj = ClipperLib::pftNonZero; break;
161       case clipper_positive: pftSubj = ClipperLib::pftPositive; break;
162       default: pftSubj = ClipperLib::pftNegative;
163     }
164     switch (m_clipFillType)
165     {
166       case clipper_even_odd: pftClip = ClipperLib::pftEvenOdd; break;
167       case clipper_non_zero: pftClip = ClipperLib::pftNonZero; break;
168       case clipper_positive: pftClip = ClipperLib::pftPositive; break;
169       default: pftClip = ClipperLib::pftNegative;
170     }
171 
172     m_clipper.Clear();
173     switch( m_operation ) {
174       case clipper_or:
175         {
176         m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject );
177         m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip );
178         m_clipper.Execute( ClipperLib::ctUnion , m_result , pftSubj, pftClip);
179 		break;
180         }
181       case clipper_and:
182         {
183         m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject );
184         m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip );
185         m_clipper.Execute( ClipperLib::ctIntersection , m_result, pftSubj, pftClip );
186 		break;
187         }
188       case clipper_xor:
189         {
190         m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject );
191         m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip );
192         m_clipper.Execute( ClipperLib::ctXor , m_result, pftSubj, pftClip );
193 		break;
194         }
195       case clipper_a_minus_b:
196         {
197         m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject );
198         m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip );
199         m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip );
200 		break;
201         }
202       case clipper_b_minus_a:
203         {
204         m_clipper.AddPolygons( m_poly_b , ClipperLib::ptSubject );
205         m_clipper.AddPolygons( m_poly_a , ClipperLib::ptClip );
206         m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip );
207 		break;
208         }
209     }
210     start_extracting();
211   }
212   //------------------------------------------------------------------------------
213 
214   template<class VSA, class VSB>
end_contour(ClipperLib::Polygons & p)215   void conv_clipper<VSA, VSB>::end_contour( ClipperLib::Polygons &p)
216   {
217   unsigned i, len;
218 
219   if( m_vertex_accumulator.size() < 3 ) return;
220   len = p.size();
221   p.resize(len+1);
222   p[len].resize(m_vertex_accumulator.size());
223   for( i = 0 ; i < m_vertex_accumulator.size() ; i++ )
224     p[len][i] = m_vertex_accumulator[i];
225   m_vertex_accumulator.remove_all();
226   }
227   //------------------------------------------------------------------------------
228 
229   template<class VSA, class VSB>
add_vertex_(double & x,double & y)230   void conv_clipper<VSA, VSB>::add_vertex_(double &x, double &y)
231   {
232 	  ClipperLib::IntPoint v;
233 
234 	  v.X = Round(x * m_scaling_factor);
235 	  v.Y = Round(y * m_scaling_factor);
236 	  m_vertex_accumulator.add( v );
237   }
238   //------------------------------------------------------------------------------
239 
240   template<class VSA, class VSB>
next_contour()241   bool conv_clipper<VSA, VSB>::next_contour()
242   {
243 	m_contour++;
244 	if(m_contour >= (int)m_result.size()) return false;
245 	m_vertex =-1;
246 	return true;
247 }
248 //------------------------------------------------------------------------------
249 
250   template<class VSA, class VSB>
next_vertex(double * x,double * y)251   bool conv_clipper<VSA, VSB>::next_vertex(double *x, double *y)
252   {
253     m_vertex++;
254     if(m_vertex >= (int)m_result[m_contour].size()) return false;
255     *x = (double)m_result[ m_contour ][ m_vertex ].X / m_scaling_factor;
256     *y = (double)m_result[ m_contour ][ m_vertex ].Y / m_scaling_factor;
257     return true;
258   }
259   //------------------------------------------------------------------------------
260 
261   template<class VSA, class VSB>
vertex(double * x,double * y)262   unsigned conv_clipper<VSA, VSB>::vertex(double *x, double *y)
263 {
264   if(  m_status == status_move_to )
265   {
266     if( next_contour() )
267     {
268       if(  next_vertex( x, y ) )
269       {
270         m_status =status_line_to;
271         return path_cmd_move_to;
272       }
273 	  else
274 	  {
275         m_status = status_stop;
276         return path_cmd_end_poly | path_flags_close;
277       }
278     }
279 	else
280       return path_cmd_stop;
281   }
282   else
283   {
284     if(  next_vertex( x, y ) )
285     {
286       return path_cmd_line_to;
287     }
288 	else
289     {
290       m_status = status_move_to;
291       return path_cmd_end_poly | path_flags_close;
292     }
293   }
294 }
295 //------------------------------------------------------------------------------
296 
297 
298 } //namespace agg
299 #endif //AGG_CONV_CLIPPER_INCLUDED
300