1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2011 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP
11 
12 #include <vector>
13 #include <boost/geometry/algorithms/assign.hpp>
14 #include <boost/range/algorithm/copy.hpp>
15 #include <boost/geometry/core/coordinate_type.hpp>
16 
17 namespace boost { namespace geometry
18 {
19 
20 
21 namespace detail { namespace partition
22 {
23 
24 typedef std::vector<std::size_t> index_vector_type;
25 
26 template <int Dimension, typename Box>
divide_box(Box const & box,Box & lower_box,Box & upper_box)27 inline void divide_box(Box const& box, Box& lower_box, Box& upper_box)
28 {
29     typedef typename coordinate_type<Box>::type ctype;
30 
31     // Divide input box into two parts, e.g. left/right
32     ctype two = 2;
33     ctype mid = (geometry::get<min_corner, Dimension>(box)
34             + geometry::get<max_corner, Dimension>(box)) / two;
35 
36     lower_box = box;
37     upper_box = box;
38     geometry::set<max_corner, Dimension>(lower_box, mid);
39     geometry::set<min_corner, Dimension>(upper_box, mid);
40 }
41 
42 // Divide collection into three subsets: lower, upper and oversized (not-fitting)
43 // (lower == left or bottom, upper == right or top)
44 template <typename OverlapsPolicy, typename InputCollection, typename Box>
divide_into_subsets(Box const & lower_box,Box const & upper_box,InputCollection const & collection,index_vector_type const & input,index_vector_type & lower,index_vector_type & upper,index_vector_type & exceeding)45 static inline void divide_into_subsets(Box const& lower_box, Box const& upper_box,
46         InputCollection const& collection,
47         index_vector_type const& input,
48         index_vector_type& lower,
49         index_vector_type& upper,
50         index_vector_type& exceeding)
51 {
52     typedef boost::range_iterator<index_vector_type const>::type index_iterator_type;
53 
54     for(index_iterator_type it = boost::begin(input);
55         it != boost::end(input);
56         ++it)
57     {
58         bool const lower_overlapping = OverlapsPolicy::apply(lower_box, collection[*it]);
59         bool const upper_overlapping = OverlapsPolicy::apply(upper_box, collection[*it]);
60 
61         if (lower_overlapping && upper_overlapping)
62         {
63             exceeding.push_back(*it);
64         }
65         else if (lower_overlapping)
66         {
67             lower.push_back(*it);
68         }
69         else if (upper_overlapping)
70         {
71             upper.push_back(*it);
72         }
73         else
74         {
75             // Is nowhere! Should not occur!
76             BOOST_ASSERT(true);
77         }
78     }
79 }
80 
81 
82 // Match collection with itself
83 template <typename InputCollection, typename Policy>
handle_one(InputCollection const & collection,index_vector_type const & input,Policy & policy)84 static inline void handle_one(InputCollection const& collection,
85         index_vector_type const& input,
86         Policy& policy)
87 {
88     typedef boost::range_iterator<index_vector_type const>::type index_iterator_type;
89     // Quadratic behaviour at lowest level (lowest quad, or all exceeding)
90     for(index_iterator_type it1 = boost::begin(input); it1 != boost::end(input); ++it1)
91     {
92         index_iterator_type it2 = it1;
93         for(++it2; it2 != boost::end(input); ++it2)
94         {
95             policy.apply(collection[*it1], collection[*it2]);
96         }
97     }
98 }
99 
100 // Match collection 1 with collection 2
101 template <typename InputCollection, typename Policy>
handle_two(InputCollection const & collection1,index_vector_type const & input1,InputCollection const & collection2,index_vector_type const & input2,Policy & policy)102 static inline void handle_two(
103         InputCollection const& collection1, index_vector_type const& input1,
104         InputCollection const& collection2, index_vector_type const& input2,
105         Policy& policy)
106 {
107     typedef boost::range_iterator<index_vector_type const>::type index_iterator_type;
108     for(index_iterator_type it1 = boost::begin(input1); it1 != boost::end(input1); ++it1)
109     {
110         for(index_iterator_type it2 = boost::begin(input2); it2 != boost::end(input2); ++it2)
111         {
112             policy.apply(collection1[*it1], collection2[*it2]);
113         }
114     }
115 }
116 
117 
118 template
119 <
120     int Dimension,
121     typename Box,
122     typename OverlapsPolicy,
123     typename VisitBoxPolicy
124 >
125 class partition_one_collection
126 {
127     typedef std::vector<std::size_t> index_vector_type;
128     typedef typename coordinate_type<Box>::type ctype;
129     typedef partition_one_collection
130             <
131                 1 - Dimension,
132                 Box,
133                 OverlapsPolicy,
134                 VisitBoxPolicy
135             > sub_divide;
136 
137     template <typename InputCollection, typename Policy>
next_level(Box const & box,InputCollection const & collection,index_vector_type const & input,int level,int min_elements,Policy & policy,VisitBoxPolicy & box_policy)138     static inline void next_level(Box const& box,
139             InputCollection const& collection,
140             index_vector_type const& input,
141             int level, int min_elements,
142             Policy& policy, VisitBoxPolicy& box_policy)
143     {
144         if (boost::size(input) > 0)
145         {
146             if (boost::size(input) > min_elements && level < 100)
147             {
148                 sub_divide::apply(box, collection, input, level + 1, min_elements, policy, box_policy);
149             }
150             else
151             {
152                 handle_one(collection, input, policy);
153             }
154         }
155     }
156 
157 public :
158     template <typename InputCollection, typename Policy>
apply(Box const & box,InputCollection const & collection,index_vector_type const & input,int level,int min_elements,Policy & policy,VisitBoxPolicy & box_policy)159     static inline void apply(Box const& box,
160             InputCollection const& collection,
161             index_vector_type const& input,
162             int level,
163             int min_elements,
164             Policy& policy, VisitBoxPolicy& box_policy)
165     {
166         box_policy.apply(box, level);
167 
168         Box lower_box, upper_box;
169         divide_box<Dimension>(box, lower_box, upper_box);
170 
171         index_vector_type lower, upper, exceeding;
172         divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection, input, lower, upper, exceeding);
173 
174         if (boost::size(exceeding) > 0)
175         {
176             // All what is not fitting a partition should be combined
177             // with each other, and with all which is fitting.
178             handle_one(collection, exceeding, policy);
179             handle_two(collection, exceeding, collection, lower, policy);
180             handle_two(collection, exceeding, collection, upper, policy);
181         }
182 
183         // Recursively call operation both parts
184         next_level(lower_box, collection, lower, level, min_elements, policy, box_policy);
185         next_level(upper_box, collection, upper, level, min_elements, policy, box_policy);
186     }
187 };
188 
189 
190 template
191 <
192     int Dimension,
193     typename Box,
194     typename OverlapsPolicy,
195     typename VisitBoxPolicy
196 >
197 class partition_two_collections
198 {
199     typedef std::vector<std::size_t> index_vector_type;
200     typedef typename coordinate_type<Box>::type ctype;
201     typedef partition_two_collections
202             <
203                 1 - Dimension,
204                 Box,
205                 OverlapsPolicy,
206                 VisitBoxPolicy
207             > sub_divide;
208 
209     template <typename InputCollection, typename Policy>
next_level(Box const & box,InputCollection const & collection1,index_vector_type const & input1,InputCollection const & collection2,index_vector_type const & input2,int level,int min_elements,Policy & policy,VisitBoxPolicy & box_policy)210     static inline void next_level(Box const& box,
211             InputCollection const& collection1, index_vector_type const& input1,
212             InputCollection const& collection2, index_vector_type const& input2,
213             int level, int min_elements,
214             Policy& policy, VisitBoxPolicy& box_policy)
215     {
216         if (boost::size(input1) > 0 && boost::size(input2) > 0)
217         {
218             if (boost::size(input1) > min_elements
219                 && boost::size(input2) > min_elements
220                 && level < 100)
221             {
222                 sub_divide::apply(box, collection1, input1, collection2, input2, level + 1, min_elements, policy, box_policy);
223             }
224             else
225             {
226                 box_policy.apply(box, level + 1);
227                 handle_two(collection1, input1, collection2, input2, policy);
228             }
229         }
230     }
231 
232 public :
233     template <typename InputCollection, typename Policy>
apply(Box const & box,InputCollection const & collection1,index_vector_type const & input1,InputCollection const & collection2,index_vector_type const & input2,int level,int min_elements,Policy & policy,VisitBoxPolicy & box_policy)234     static inline void apply(Box const& box,
235             InputCollection const& collection1, index_vector_type const& input1,
236             InputCollection const& collection2, index_vector_type const& input2,
237             int level,
238             int min_elements,
239             Policy& policy, VisitBoxPolicy& box_policy)
240     {
241         box_policy.apply(box, level);
242 
243         Box lower_box, upper_box;
244         divide_box<Dimension>(box, lower_box, upper_box);
245 
246         index_vector_type lower1, upper1, exceeding1;
247         index_vector_type lower2, upper2, exceeding2;
248         divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection1, input1, lower1, upper1, exceeding1);
249         divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection2, input2, lower2, upper2, exceeding2);
250 
251         if (boost::size(exceeding1) > 0)
252         {
253             // All exceeding from 1 with 2:
254             handle_two(collection1, exceeding1, collection2, exceeding2, policy);
255 
256             // All exceeding from 1 with lower and upper of 2:
257             handle_two(collection1, exceeding1, collection2, lower2, policy);
258             handle_two(collection1, exceeding1, collection2, upper2, policy);
259         }
260         if (boost::size(exceeding2) > 0)
261         {
262             // All exceeding from 2 with lower and upper of 1:
263             handle_two(collection1, lower1, collection2, exceeding2, policy);
264             handle_two(collection1, upper1, collection2, exceeding2, policy);
265         }
266 
267         next_level(lower_box, collection1, lower1, collection2, lower2, level, min_elements, policy, box_policy);
268         next_level(upper_box, collection1, upper1, collection2, upper2, level, min_elements, policy, box_policy);
269     }
270 };
271 
272 
273 
274 }} // namespace detail::partition
275 
276 struct visit_no_policy
277 {
278     template <typename Box>
applyboost::geometry::visit_no_policy279     static inline void apply(Box const&, int )
280     {}
281 };
282 
283 
284 template
285 <
286     typename Box,
287     typename ExpandPolicy,
288     typename OverlapsPolicy,
289     typename VisitBoxPolicy = visit_no_policy
290 >
291 class partition
292 {
293     typedef std::vector<std::size_t> index_vector_type;
294 
295     template <typename InputCollection>
expand_to_collection(InputCollection const & collection,Box & total,index_vector_type & index_vector)296     static inline void expand_to_collection(InputCollection const& collection, Box& total, index_vector_type& index_vector)
297     {
298         std::size_t index = 0;
299         for(typename boost::range_iterator<InputCollection const>::type it
300             = boost::begin(collection);
301             it != boost::end(collection);
302             ++it, ++index)
303         {
304             ExpandPolicy::apply(total, *it);
305             index_vector.push_back(index);
306         }
307     }
308 
309 
310 public :
311     template <typename InputCollection, typename VisitPolicy>
apply(InputCollection const & collection,VisitPolicy & visitor,int min_elements=16,VisitBoxPolicy box_visitor=visit_no_policy ())312     static inline void apply(InputCollection const& collection,
313             VisitPolicy& visitor,
314             int min_elements = 16,
315             VisitBoxPolicy box_visitor = visit_no_policy()
316             )
317     {
318         if (boost::size(collection) > min_elements)
319         {
320             index_vector_type index_vector;
321             Box total;
322             assign_inverse(total);
323             expand_to_collection(collection, total, index_vector);
324 
325             detail::partition::partition_one_collection
326                 <
327                     0, Box,
328                     OverlapsPolicy,
329                     VisitBoxPolicy
330                 >::apply(total, collection, index_vector, 0, min_elements, visitor, box_visitor);
331         }
332         else
333         {
334             typedef typename boost::range_iterator<InputCollection const>::type iterator_type;
335             for(iterator_type it1 = boost::begin(collection); it1 != boost::end(collection); ++it1)
336             {
337                 iterator_type it2 = it1;
338                 for(++it2; it2 != boost::end(collection); ++it2)
339                 {
340                     visitor.apply(*it1, *it2);
341                 }
342             }
343         }
344     }
345 
346     template <typename InputCollection, typename VisitPolicy>
apply(InputCollection const & collection1,InputCollection const & collection2,VisitPolicy & visitor,int min_elements=16,VisitBoxPolicy box_visitor=visit_no_policy ())347     static inline void apply(InputCollection const& collection1,
348                 InputCollection const& collection2,
349                 VisitPolicy& visitor,
350                 int min_elements = 16,
351                 VisitBoxPolicy box_visitor = visit_no_policy()
352                 )
353     {
354         if (boost::size(collection1) > min_elements && boost::size(collection2) > min_elements)
355         {
356             index_vector_type index_vector1, index_vector2;
357             Box total;
358             assign_inverse(total);
359             expand_to_collection(collection1, total, index_vector1);
360             expand_to_collection(collection2, total, index_vector2);
361 
362             detail::partition::partition_two_collections
363                 <
364                     0, Box, OverlapsPolicy, VisitBoxPolicy
365                 >::apply(total,
366                     collection1, index_vector1,
367                     collection2, index_vector2,
368                     0, min_elements, visitor, box_visitor);
369         }
370         else
371         {
372             typedef typename boost::range_iterator<InputCollection const>::type iterator_type;
373             for(iterator_type it1 = boost::begin(collection1); it1 != boost::end(collection1); ++it1)
374             {
375                 for(iterator_type it2 = boost::begin(collection2); it2 != boost::end(collection2); ++it2)
376                 {
377                     visitor.apply(*it1, *it2);
378                 }
379             }
380         }
381     }
382 
383 };
384 
385 
386 }} // namespace boost::geometry
387 
388 
389 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RING_IDENTIFIER_HPP
390