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 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
8 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
9 
10 // Use, modification and distribution is subject to the Boost Software License,
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
12 // http://www.boost.org/LICENSE_1_0.txt)
13 
14 #ifndef BOOST_GEOMETRY_ITERATORS_EVER_CIRCLING_ITERATOR_HPP
15 #define BOOST_GEOMETRY_ITERATORS_EVER_CIRCLING_ITERATOR_HPP
16 
17 #include <boost/range.hpp>
18 #include <boost/iterator.hpp>
19 #include <boost/iterator/iterator_adaptor.hpp>
20 #include <boost/iterator/iterator_categories.hpp>
21 
22 #include <boost/geometry/iterators/base.hpp>
23 
24 namespace boost { namespace geometry
25 {
26 
27 /*!
28     \brief Iterator which ever circles through a range
29     \tparam Iterator iterator on which this class is based on
30     \ingroup iterators
31     \details If the iterator arrives at range.end() it restarts from the
32      beginning. So it has to be stopped in another way.
33     Don't call for(....; it++) because it will turn in an endless loop
34     \note Name inspired on David Bowie's
35     "Chant Of The Ever Circling Skeletal Family"
36 */
37 template <typename Iterator>
38 struct ever_circling_iterator :
39     public detail::iterators::iterator_base
40     <
41         ever_circling_iterator<Iterator>,
42         Iterator
43     >
44 {
45     friend class boost::iterator_core_access;
46 
ever_circling_iteratorboost::geometry::ever_circling_iterator47     explicit inline ever_circling_iterator(Iterator begin, Iterator end,
48             bool skip_first = false)
49       : m_begin(begin)
50       , m_end(end)
51       , m_skip_first(skip_first)
52     {
53         this->base_reference() = begin;
54     }
55 
ever_circling_iteratorboost::geometry::ever_circling_iterator56     explicit inline ever_circling_iterator(Iterator begin, Iterator end, Iterator start,
57             bool skip_first = false)
58       : m_begin(begin)
59       , m_end(end)
60       , m_skip_first(skip_first)
61     {
62         this->base_reference() = start;
63     }
64 
65     /// Navigate to a certain position, should be in [start .. end], if at end
66     /// it will circle again.
movetoboost::geometry::ever_circling_iterator67     inline void moveto(Iterator it)
68     {
69         this->base_reference() = it;
70         check_end();
71     }
72 
73 private:
74 
incrementboost::geometry::ever_circling_iterator75     inline void increment(bool possibly_skip = true)
76     {
77         (this->base_reference())++;
78         check_end(possibly_skip);
79     }
80 
check_endboost::geometry::ever_circling_iterator81     inline void check_end(bool possibly_skip = true)
82     {
83         if (this->base() == this->m_end)
84         {
85             this->base_reference() = this->m_begin;
86             if (m_skip_first && possibly_skip)
87             {
88                 increment(false);
89             }
90         }
91     }
92 
93     Iterator m_begin;
94     Iterator m_end;
95     bool m_skip_first;
96 };
97 
98 template <typename Range>
99 struct ever_circling_range_iterator
100     : public boost::iterator_facade
101     <
102         ever_circling_range_iterator<Range>,
103         typename boost::range_value<Range>::type const,
104         boost::random_access_traversal_tag
105     >
106 {
107     /// Constructor including the range it is based on
ever_circling_range_iteratorboost::geometry::ever_circling_range_iterator108     explicit inline ever_circling_range_iterator(Range& range)
109         : m_range(&range)
110         , m_iterator(boost::begin(range))
111         , m_size(boost::size(range))
112         , m_index(0)
113     {}
114 
115     /// Default constructor
ever_circling_range_iteratorboost::geometry::ever_circling_range_iterator116     explicit inline ever_circling_range_iterator()
117         : m_range(NULL)
118         , m_size(0)
119         , m_index(0)
120     {}
121 
122     typedef std::ptrdiff_t difference_type;
123 
124 private:
125     friend class boost::iterator_core_access;
126 
dereferenceboost::geometry::ever_circling_range_iterator127     inline typename boost::range_value<Range>::type const& dereference() const
128     {
129         return *m_iterator;
130     }
131 
distance_toboost::geometry::ever_circling_range_iterator132     inline difference_type distance_to(ever_circling_range_iterator<Range> const& other) const
133     {
134         return other.m_index - this->m_index;
135     }
136 
equalboost::geometry::ever_circling_range_iterator137     inline bool equal(ever_circling_range_iterator<Range> const& other) const
138     {
139         return this->m_range == other.m_range
140             && this->m_index == other.m_index;
141     }
142 
incrementboost::geometry::ever_circling_range_iterator143     inline void increment()
144     {
145         ++m_index;
146         if (m_index >= 0 && m_index < m_size)
147         {
148             ++m_iterator;
149         }
150         else
151         {
152             update_iterator();
153         }
154     }
155 
decrementboost::geometry::ever_circling_range_iterator156     inline void decrement()
157     {
158         --m_index;
159         if (m_index >= 0 && m_index < m_size)
160         {
161             --m_iterator;
162         }
163         else
164         {
165             update_iterator();
166         }
167     }
168 
advanceboost::geometry::ever_circling_range_iterator169     inline void advance(difference_type n)
170     {
171         if (m_index >= 0 && m_index < m_size
172             && m_index + n >= 0 && m_index + n < m_size)
173         {
174             m_index += n;
175             m_iterator += n;
176         }
177         else
178         {
179             m_index += n;
180             update_iterator();
181         }
182     }
183 
update_iteratorboost::geometry::ever_circling_range_iterator184     inline void update_iterator()
185     {
186         while (m_index < 0)
187         {
188             m_index += m_size;
189         }
190         m_index = m_index % m_size;
191         this->m_iterator = boost::begin(*m_range) + m_index;
192     }
193 
194     Range* m_range;
195     typename boost::range_iterator<Range>::type m_iterator;
196     difference_type m_size;
197     difference_type m_index;
198 };
199 
200 
201 }} // namespace boost::geometry
202 
203 #endif // BOOST_GEOMETRY_ITERATORS_EVER_CIRCLING_ITERATOR_HPP
204