1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
6 
7 // Use, modification and distribution is subject to the Boost Software License,
8 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 //
11 // Custom polygon example
12 
13 #include <iostream>
14 
15 #include <boost/assert.hpp>
16 
17 #include <boost/iterator.hpp>
18 #include <boost/iterator/iterator_adaptor.hpp>
19 #include <boost/iterator/iterator_categories.hpp>
20 #include <boost/iterator/iterator_facade.hpp>
21 
22 
23 #include <boost/geometry/geometry.hpp>
24 #include <boost/geometry/geometries/register/point.hpp>
25 #include <boost/geometry/geometries/register/ring.hpp>
26 #include <boost/geometry/util/add_const_if_c.hpp>
27 
28 // Sample point, having x/y
29 struct my_point
30 {
my_pointmy_point31     my_point(double a = 0, double b = 0)
32         : x(a), y(b)
33     {}
34     double x,y;
35 };
36 
37 // Sample polygon, having legacy methods
38 // (similar to e.g. COM objects)
39 class my_polygon
40 {
41     std::vector<my_point> points;
42     public :
add_point(my_point const & p)43         void add_point(my_point const& p) { points.push_back(p); }
44 
45         // Const access
get_point(std::size_t i) const46         my_point const& get_point(std::size_t i) const
47         {
48             BOOST_ASSERT(i < points.size());
49             return points[i];
50         }
51 
52         // Mutable access
get_point(std::size_t i)53         my_point & get_point(std::size_t i)
54         {
55             BOOST_ASSERT(i < points.size());
56             return points[i];
57         }
58 
59 
point_count() const60         int point_count() const { return points.size(); }
erase_all()61         void erase_all() { points.clear(); }
62 
set_size(int n)63         inline void set_size(int n) { points.resize(n); }
64 };
65 
66 // ----------------------------------------------------------------------------
67 // Adaption: implement iterator and range-extension, and register with Boost.Geometry
68 
69 // 1) implement iterator (const and non-const versions)
70 template<typename MyPolygon>
71 struct custom_iterator : public boost::iterator_facade
72                             <
73                                 custom_iterator<MyPolygon>,
74                                 my_point,
75                                 boost::random_access_traversal_tag,
76                                 typename boost::mpl::if_
77                                     <
78                                         boost::is_const<MyPolygon>,
79                                         my_point const,
80                                         my_point
81                                     >::type&
82                             >
83 {
84     // Constructor for begin()
custom_iteratorcustom_iterator85     explicit custom_iterator(MyPolygon& polygon)
86         : m_polygon(&polygon)
87         , m_index(0)
88     {}
89 
90     // Constructor for end()
custom_iteratorcustom_iterator91     explicit custom_iterator(bool, MyPolygon& polygon)
92         : m_polygon(&polygon)
93         , m_index(polygon.point_count())
94     {}
95 
96 
97     // Default constructor
custom_iteratorcustom_iterator98     explicit custom_iterator()
99         : m_polygon(NULL)
100         , m_index(-1)
101     {}
102 
103     typedef typename boost::mpl::if_
104         <
105             boost::is_const<MyPolygon>,
106             my_point const,
107             my_point
108         >::type my_point_type;
109 
110 private:
111     friend class boost::iterator_core_access;
112 
113 
114     typedef boost::iterator_facade
115         <
116             custom_iterator<MyPolygon>,
117             my_point,
118             boost::random_access_traversal_tag,
119             my_point_type&
120         > facade;
121 
122     MyPolygon* m_polygon;
123     int m_index;
124 
equalcustom_iterator125     bool equal(custom_iterator const& other) const
126     {
127         return this->m_index == other.m_index;
128     }
distance_tocustom_iterator129     typename facade::difference_type distance_to(custom_iterator const& other) const
130     {
131         return other.m_index - this->m_index;
132     }
133 
advancecustom_iterator134     void advance(typename facade::difference_type n)
135     {
136         m_index += n;
137         if(m_polygon != NULL
138             && (m_index >= m_polygon->point_count()
139             || m_index < 0)
140             )
141         {
142             m_index = m_polygon->point_count();
143         }
144     }
145 
incrementcustom_iterator146     void increment()
147     {
148         advance(1);
149     }
150 
decrementcustom_iterator151     void decrement()
152     {
153         advance(-1);
154     }
155 
156     // const and non-const dereference of this iterator
dereferencecustom_iterator157     my_point_type& dereference() const
158     {
159         return m_polygon->get_point(m_index);
160     }
161 };
162 
163 
164 
165 
166 // 2) Implement Boost.Range const functionality
167 //    using method 2, "provide free-standing functions and specialize metafunctions"
168 // 2a) meta-functions
169 namespace boost
170 {
171     template<> struct range_mutable_iterator<my_polygon>
172     {
173         typedef custom_iterator<my_polygon> type;
174     };
175 
176     template<> struct range_const_iterator<my_polygon>
177     {
178         typedef custom_iterator<my_polygon const> type;
179     };
180 
181     // RangeEx
182     template<> struct range_size<my_polygon>
183     {
184         typedef std::size_t type;
185     };
186 
187 } // namespace 'boost'
188 
189 
190 // 2b) free-standing function for Boost.Range ADP
range_begin(my_polygon & polygon)191 inline custom_iterator<my_polygon> range_begin(my_polygon& polygon)
192 {
193     return custom_iterator<my_polygon>(polygon);
194 }
195 
range_begin(my_polygon const & polygon)196 inline custom_iterator<my_polygon const> range_begin(my_polygon const& polygon)
197 {
198     return custom_iterator<my_polygon const>(polygon);
199 }
200 
range_end(my_polygon & polygon)201 inline custom_iterator<my_polygon> range_end(my_polygon& polygon)
202 {
203     return custom_iterator<my_polygon>(true, polygon);
204 }
205 
range_end(my_polygon const & polygon)206 inline custom_iterator<my_polygon const> range_end(my_polygon const& polygon)
207 {
208     return custom_iterator<my_polygon const>(true, polygon);
209 }
210 
211 
212 
213 // 3) optional, for writable geometries only, implement push_back/resize/clear
214 namespace boost { namespace geometry { namespace traits
215 {
216 
217 template<> struct push_back<my_polygon>
218 {
applyboost::geometry::traits::push_back219     static inline void apply(my_polygon& polygon, my_point const& point)
220     {
221         polygon.add_point(point);
222     }
223 };
224 
225 template<> struct resize<my_polygon>
226 {
applyboost::geometry::traits::resize227     static inline void apply(my_polygon& polygon, std::size_t new_size)
228     {
229         polygon.set_size(new_size);
230     }
231 };
232 
233 template<> struct clear<my_polygon>
234 {
applyboost::geometry::traits::clear235     static inline void apply(my_polygon& polygon)
236     {
237         polygon.erase_all();
238     }
239 };
240 
241 }}}
242 
243 
244 // 4) register with Boost.Geometry
BOOST_GEOMETRY_REGISTER_POINT_2D(my_point,double,cs::cartesian,x,y)245 BOOST_GEOMETRY_REGISTER_POINT_2D(my_point, double, cs::cartesian, x, y)
246 
247 BOOST_GEOMETRY_REGISTER_RING(my_polygon)
248 
249 
250 // end adaption
251 // ----------------------------------------------------------------------------
252 
253 
254 void walk_using_iterator(my_polygon const& polygon)
255 {
256     for (custom_iterator<my_polygon const> it = custom_iterator<my_polygon const>(polygon);
257         it != custom_iterator<my_polygon const>(true, polygon);
258         ++it)
259     {
260         std::cout << boost::geometry::dsv(*it) << std::endl;
261     }
262     std::cout << std::endl;
263 }
264 
265 
walk_using_range(my_polygon const & polygon)266 void walk_using_range(my_polygon const& polygon)
267 {
268     for (boost::range_iterator<my_polygon const>::type it
269             = boost::begin(polygon);
270         it != boost::end(polygon);
271         ++it)
272     {
273         std::cout << boost::geometry::dsv(*it) << std::endl;
274     }
275     std::cout << std::endl;
276 }
277 
278 
main()279 int main()
280 {
281     my_polygon container1;
282 
283     // Create (as an example) a regular polygon
284     const int n = 5;
285     const double d = (360 / n) * boost::geometry::math::d2r;
286     double a = 0;
287     for (int i = 0; i < n + 1; i++, a += d)
288     {
289         container1.add_point(my_point(sin(a), cos(a)));
290     }
291 
292     std::cout << "Walk using Boost.Iterator derivative" << std::endl;
293     walk_using_iterator(container1);
294 
295     std::cout << "Walk using Boost.Range extension" << std::endl << std::endl;
296     walk_using_range(container1);
297 
298     std::cout << "Use it by Boost.Geometry" << std::endl;
299     std::cout << "Area: " << boost::geometry::area(container1) << std::endl;
300 
301     // Container 2 will be modified by Boost.Geometry. Add all points but the last one.
302     my_polygon container2;
303     for (int i = 0; i < n; i++)
304     {
305         // Use here the Boost.Geometry internal way of inserting (but the my_polygon way of getting)
306         boost::geometry::traits::push_back<my_polygon>::apply(container2, container1.get_point(i));
307     }
308 
309     std::cout << "Second container is not closed:" << std::endl;
310     walk_using_range(container2);
311 
312     // Correct (= close it)
313     boost::geometry::correct(container2);
314 
315     std::cout << "Now it is closed:" << std::endl;
316     walk_using_range(container2);
317     std::cout << "Area: " << boost::geometry::area(container2) << std::endl;
318 
319     // Use things from std:: using Boost.Range
320     std::reverse(boost::begin(container2), boost::end(container2));
321     std::cout << "Area reversed: " << boost::geometry::area(container2) << std::endl;
322 
323     return 0;
324 }
325