Chris@16
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@16
|
2
|
Chris@16
|
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@16
|
4
|
Chris@101
|
5 // This file was modified by Oracle on 2014.
|
Chris@101
|
6 // Modifications copyright (c) 2014 Oracle and/or its affiliates.
|
Chris@101
|
7
|
Chris@101
|
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
Chris@101
|
9
|
Chris@16
|
10 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
|
Chris@16
|
11 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
|
Chris@16
|
12
|
Chris@16
|
13 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
14 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
15 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
16
|
Chris@16
|
17 #ifndef BOOST_GEOMETRY_STRATEGIES_AGNOSTIC_CONVEX_GRAHAM_ANDREW_HPP
|
Chris@16
|
18 #define BOOST_GEOMETRY_STRATEGIES_AGNOSTIC_CONVEX_GRAHAM_ANDREW_HPP
|
Chris@16
|
19
|
Chris@16
|
20
|
Chris@16
|
21 #include <cstddef>
|
Chris@16
|
22 #include <algorithm>
|
Chris@16
|
23 #include <vector>
|
Chris@16
|
24
|
Chris@16
|
25 #include <boost/range.hpp>
|
Chris@16
|
26
|
Chris@16
|
27 #include <boost/geometry/core/cs.hpp>
|
Chris@16
|
28 #include <boost/geometry/core/point_type.hpp>
|
Chris@16
|
29 #include <boost/geometry/strategies/convex_hull.hpp>
|
Chris@16
|
30
|
Chris@16
|
31 #include <boost/geometry/views/detail/range_type.hpp>
|
Chris@16
|
32
|
Chris@16
|
33 #include <boost/geometry/policies/compare.hpp>
|
Chris@16
|
34
|
Chris@16
|
35 #include <boost/geometry/algorithms/detail/for_each_range.hpp>
|
Chris@16
|
36 #include <boost/geometry/views/reversible_view.hpp>
|
Chris@16
|
37
|
Chris@16
|
38
|
Chris@16
|
39 namespace boost { namespace geometry
|
Chris@16
|
40 {
|
Chris@16
|
41
|
Chris@16
|
42 namespace strategy { namespace convex_hull
|
Chris@16
|
43 {
|
Chris@16
|
44
|
Chris@16
|
45 #ifndef DOXYGEN_NO_DETAIL
|
Chris@16
|
46 namespace detail
|
Chris@16
|
47 {
|
Chris@16
|
48
|
Chris@16
|
49
|
Chris@16
|
50 template
|
Chris@16
|
51 <
|
Chris@16
|
52 typename InputRange,
|
Chris@16
|
53 typename RangeIterator,
|
Chris@16
|
54 typename StrategyLess,
|
Chris@16
|
55 typename StrategyGreater
|
Chris@16
|
56 >
|
Chris@16
|
57 struct get_extremes
|
Chris@16
|
58 {
|
Chris@16
|
59 typedef typename point_type<InputRange>::type point_type;
|
Chris@16
|
60
|
Chris@16
|
61 point_type left, right;
|
Chris@16
|
62
|
Chris@16
|
63 bool first;
|
Chris@16
|
64
|
Chris@16
|
65 StrategyLess less;
|
Chris@16
|
66 StrategyGreater greater;
|
Chris@16
|
67
|
Chris@16
|
68 inline get_extremes()
|
Chris@16
|
69 : first(true)
|
Chris@16
|
70 {}
|
Chris@16
|
71
|
Chris@16
|
72 inline void apply(InputRange const& range)
|
Chris@16
|
73 {
|
Chris@16
|
74 if (boost::size(range) == 0)
|
Chris@16
|
75 {
|
Chris@16
|
76 return;
|
Chris@16
|
77 }
|
Chris@16
|
78
|
Chris@16
|
79 // First iterate through this range
|
Chris@16
|
80 // (this two-stage approach avoids many point copies,
|
Chris@16
|
81 // because iterators are kept in memory. Because iterators are
|
Chris@16
|
82 // not persistent (in MSVC) this approach is not applicable
|
Chris@16
|
83 // for more ranges together)
|
Chris@16
|
84
|
Chris@16
|
85 RangeIterator left_it = boost::begin(range);
|
Chris@16
|
86 RangeIterator right_it = boost::begin(range);
|
Chris@16
|
87
|
Chris@16
|
88 for (RangeIterator it = boost::begin(range) + 1;
|
Chris@16
|
89 it != boost::end(range);
|
Chris@16
|
90 ++it)
|
Chris@16
|
91 {
|
Chris@16
|
92 if (less(*it, *left_it))
|
Chris@16
|
93 {
|
Chris@16
|
94 left_it = it;
|
Chris@16
|
95 }
|
Chris@16
|
96
|
Chris@16
|
97 if (greater(*it, *right_it))
|
Chris@16
|
98 {
|
Chris@16
|
99 right_it = it;
|
Chris@16
|
100 }
|
Chris@16
|
101 }
|
Chris@16
|
102
|
Chris@16
|
103 // Then compare with earlier
|
Chris@16
|
104 if (first)
|
Chris@16
|
105 {
|
Chris@16
|
106 // First time, assign left/right
|
Chris@16
|
107 left = *left_it;
|
Chris@16
|
108 right = *right_it;
|
Chris@16
|
109 first = false;
|
Chris@16
|
110 }
|
Chris@16
|
111 else
|
Chris@16
|
112 {
|
Chris@16
|
113 // Next time, check if this range was left/right from
|
Chris@16
|
114 // the extremes already collected
|
Chris@16
|
115 if (less(*left_it, left))
|
Chris@16
|
116 {
|
Chris@16
|
117 left = *left_it;
|
Chris@16
|
118 }
|
Chris@16
|
119
|
Chris@16
|
120 if (greater(*right_it, right))
|
Chris@16
|
121 {
|
Chris@16
|
122 right = *right_it;
|
Chris@16
|
123 }
|
Chris@16
|
124 }
|
Chris@16
|
125 }
|
Chris@16
|
126 };
|
Chris@16
|
127
|
Chris@16
|
128
|
Chris@16
|
129 template
|
Chris@16
|
130 <
|
Chris@16
|
131 typename InputRange,
|
Chris@16
|
132 typename RangeIterator,
|
Chris@16
|
133 typename Container,
|
Chris@16
|
134 typename SideStrategy
|
Chris@16
|
135 >
|
Chris@16
|
136 struct assign_range
|
Chris@16
|
137 {
|
Chris@16
|
138 Container lower_points, upper_points;
|
Chris@16
|
139
|
Chris@16
|
140 typedef typename point_type<InputRange>::type point_type;
|
Chris@16
|
141
|
Chris@16
|
142 point_type const& most_left;
|
Chris@16
|
143 point_type const& most_right;
|
Chris@16
|
144
|
Chris@16
|
145 inline assign_range(point_type const& left, point_type const& right)
|
Chris@16
|
146 : most_left(left)
|
Chris@16
|
147 , most_right(right)
|
Chris@16
|
148 {}
|
Chris@16
|
149
|
Chris@16
|
150 inline void apply(InputRange const& range)
|
Chris@16
|
151 {
|
Chris@16
|
152 typedef SideStrategy side;
|
Chris@16
|
153
|
Chris@16
|
154 // Put points in one of the two output sequences
|
Chris@16
|
155 for (RangeIterator it = boost::begin(range);
|
Chris@16
|
156 it != boost::end(range);
|
Chris@16
|
157 ++it)
|
Chris@16
|
158 {
|
Chris@16
|
159 // check if it is lying most_left or most_right from the line
|
Chris@16
|
160
|
Chris@16
|
161 int dir = side::apply(most_left, most_right, *it);
|
Chris@16
|
162 switch(dir)
|
Chris@16
|
163 {
|
Chris@16
|
164 case 1 : // left side
|
Chris@16
|
165 upper_points.push_back(*it);
|
Chris@16
|
166 break;
|
Chris@16
|
167 case -1 : // right side
|
Chris@16
|
168 lower_points.push_back(*it);
|
Chris@16
|
169 break;
|
Chris@16
|
170
|
Chris@16
|
171 // 0: on line most_left-most_right,
|
Chris@16
|
172 // or most_left, or most_right,
|
Chris@16
|
173 // -> all never part of hull
|
Chris@16
|
174 }
|
Chris@16
|
175 }
|
Chris@16
|
176 }
|
Chris@16
|
177 };
|
Chris@16
|
178
|
Chris@16
|
179 template <typename Range>
|
Chris@16
|
180 static inline void sort(Range& range)
|
Chris@16
|
181 {
|
Chris@16
|
182 typedef typename boost::range_value<Range>::type point_type;
|
Chris@16
|
183 typedef geometry::less<point_type> comparator;
|
Chris@16
|
184
|
Chris@16
|
185 std::sort(boost::begin(range), boost::end(range), comparator());
|
Chris@16
|
186 }
|
Chris@16
|
187
|
Chris@16
|
188 } // namespace detail
|
Chris@16
|
189 #endif // DOXYGEN_NO_DETAIL
|
Chris@16
|
190
|
Chris@16
|
191
|
Chris@16
|
192 /*!
|
Chris@16
|
193 \brief Graham scan strategy to calculate convex hull
|
Chris@16
|
194 \ingroup strategies
|
Chris@16
|
195 \note Completely reworked version inspired on the sources listed below
|
Chris@16
|
196 \see http://www.ddj.com/architect/201806315
|
Chris@16
|
197 \see http://marknelson.us/2007/08/22/convex
|
Chris@16
|
198 */
|
Chris@16
|
199 template <typename InputGeometry, typename OutputPoint>
|
Chris@16
|
200 class graham_andrew
|
Chris@16
|
201 {
|
Chris@16
|
202 public :
|
Chris@16
|
203 typedef OutputPoint point_type;
|
Chris@16
|
204 typedef InputGeometry geometry_type;
|
Chris@16
|
205
|
Chris@16
|
206 private:
|
Chris@16
|
207
|
Chris@16
|
208 typedef typename cs_tag<point_type>::type cs_tag;
|
Chris@16
|
209
|
Chris@16
|
210 typedef typename std::vector<point_type> container_type;
|
Chris@16
|
211 typedef typename std::vector<point_type>::const_iterator iterator;
|
Chris@16
|
212 typedef typename std::vector<point_type>::const_reverse_iterator rev_iterator;
|
Chris@16
|
213
|
Chris@16
|
214
|
Chris@16
|
215 class partitions
|
Chris@16
|
216 {
|
Chris@16
|
217 friend class graham_andrew;
|
Chris@16
|
218
|
Chris@16
|
219 container_type m_lower_hull;
|
Chris@16
|
220 container_type m_upper_hull;
|
Chris@16
|
221 container_type m_copied_input;
|
Chris@16
|
222 };
|
Chris@16
|
223
|
Chris@16
|
224
|
Chris@16
|
225 public:
|
Chris@16
|
226 typedef partitions state_type;
|
Chris@16
|
227
|
Chris@16
|
228
|
Chris@16
|
229 inline void apply(InputGeometry const& geometry, partitions& state) const
|
Chris@16
|
230 {
|
Chris@16
|
231 // First pass.
|
Chris@16
|
232 // Get min/max (in most cases left / right) points
|
Chris@16
|
233 // This makes use of the geometry::less/greater predicates
|
Chris@16
|
234
|
Chris@16
|
235 // For the left boundary it is important that multiple points
|
Chris@16
|
236 // are sorted from bottom to top. Therefore the less predicate
|
Chris@16
|
237 // does not take the x-only template parameter (this fixes ticket #6019.
|
Chris@101
|
238 // For the right boundary it is not necessary (though also not harmful),
|
Chris@16
|
239 // because points are sorted from bottom to top in a later stage.
|
Chris@16
|
240 // For symmetry and to get often more balanced lower/upper halves
|
Chris@16
|
241 // we keep it.
|
Chris@16
|
242
|
Chris@16
|
243 typedef typename geometry::detail::range_type<InputGeometry>::type range_type;
|
Chris@16
|
244
|
Chris@16
|
245 typedef typename boost::range_iterator
|
Chris@16
|
246 <
|
Chris@16
|
247 range_type const
|
Chris@16
|
248 >::type range_iterator;
|
Chris@16
|
249
|
Chris@16
|
250 detail::get_extremes
|
Chris@16
|
251 <
|
Chris@16
|
252 range_type,
|
Chris@16
|
253 range_iterator,
|
Chris@16
|
254 geometry::less<point_type>,
|
Chris@16
|
255 geometry::greater<point_type>
|
Chris@16
|
256 > extremes;
|
Chris@16
|
257 geometry::detail::for_each_range(geometry, extremes);
|
Chris@16
|
258
|
Chris@16
|
259 // Bounding left/right points
|
Chris@16
|
260 // Second pass, now that extremes are found, assign all points
|
Chris@16
|
261 // in either lower, either upper
|
Chris@16
|
262 detail::assign_range
|
Chris@16
|
263 <
|
Chris@16
|
264 range_type,
|
Chris@16
|
265 range_iterator,
|
Chris@16
|
266 container_type,
|
Chris@16
|
267 typename strategy::side::services::default_strategy<cs_tag>::type
|
Chris@16
|
268 > assigner(extremes.left, extremes.right);
|
Chris@16
|
269
|
Chris@16
|
270 geometry::detail::for_each_range(geometry, assigner);
|
Chris@16
|
271
|
Chris@16
|
272
|
Chris@16
|
273 // Sort both collections, first on x(, then on y)
|
Chris@16
|
274 detail::sort(assigner.lower_points);
|
Chris@16
|
275 detail::sort(assigner.upper_points);
|
Chris@16
|
276
|
Chris@16
|
277 //std::cout << boost::size(assigner.lower_points) << std::endl;
|
Chris@16
|
278 //std::cout << boost::size(assigner.upper_points) << std::endl;
|
Chris@16
|
279
|
Chris@16
|
280 // And decide which point should be in the final hull
|
Chris@16
|
281 build_half_hull<-1>(assigner.lower_points, state.m_lower_hull,
|
Chris@16
|
282 extremes.left, extremes.right);
|
Chris@16
|
283 build_half_hull<1>(assigner.upper_points, state.m_upper_hull,
|
Chris@16
|
284 extremes.left, extremes.right);
|
Chris@16
|
285 }
|
Chris@16
|
286
|
Chris@16
|
287
|
Chris@16
|
288 template <typename OutputIterator>
|
Chris@16
|
289 inline void result(partitions const& state,
|
Chris@101
|
290 OutputIterator out,
|
Chris@101
|
291 bool clockwise,
|
Chris@101
|
292 bool closed) const
|
Chris@16
|
293 {
|
Chris@16
|
294 if (clockwise)
|
Chris@16
|
295 {
|
Chris@101
|
296 output_ranges(state.m_upper_hull, state.m_lower_hull, out, closed);
|
Chris@16
|
297 }
|
Chris@16
|
298 else
|
Chris@16
|
299 {
|
Chris@101
|
300 output_ranges(state.m_lower_hull, state.m_upper_hull, out, closed);
|
Chris@16
|
301 }
|
Chris@16
|
302 }
|
Chris@16
|
303
|
Chris@16
|
304
|
Chris@16
|
305 private:
|
Chris@16
|
306
|
Chris@16
|
307 template <int Factor>
|
Chris@16
|
308 static inline void build_half_hull(container_type const& input,
|
Chris@16
|
309 container_type& output,
|
Chris@16
|
310 point_type const& left, point_type const& right)
|
Chris@16
|
311 {
|
Chris@16
|
312 output.push_back(left);
|
Chris@16
|
313 for(iterator it = input.begin(); it != input.end(); ++it)
|
Chris@16
|
314 {
|
Chris@16
|
315 add_to_hull<Factor>(*it, output);
|
Chris@16
|
316 }
|
Chris@16
|
317 add_to_hull<Factor>(right, output);
|
Chris@16
|
318 }
|
Chris@16
|
319
|
Chris@16
|
320
|
Chris@16
|
321 template <int Factor>
|
Chris@16
|
322 static inline void add_to_hull(point_type const& p, container_type& output)
|
Chris@16
|
323 {
|
Chris@16
|
324 typedef typename strategy::side::services::default_strategy<cs_tag>::type side;
|
Chris@16
|
325
|
Chris@16
|
326 output.push_back(p);
|
Chris@101
|
327 std::size_t output_size = output.size();
|
Chris@16
|
328 while (output_size >= 3)
|
Chris@16
|
329 {
|
Chris@16
|
330 rev_iterator rit = output.rbegin();
|
Chris@101
|
331 point_type const last = *rit++;
|
Chris@16
|
332 point_type const& last2 = *rit++;
|
Chris@16
|
333
|
Chris@16
|
334 if (Factor * side::apply(*rit, last, last2) <= 0)
|
Chris@16
|
335 {
|
Chris@16
|
336 // Remove last two points from stack, and add last again
|
Chris@16
|
337 // This is much faster then erasing the one but last.
|
Chris@16
|
338 output.pop_back();
|
Chris@16
|
339 output.pop_back();
|
Chris@16
|
340 output.push_back(last);
|
Chris@16
|
341 output_size--;
|
Chris@16
|
342 }
|
Chris@16
|
343 else
|
Chris@16
|
344 {
|
Chris@16
|
345 return;
|
Chris@16
|
346 }
|
Chris@16
|
347 }
|
Chris@16
|
348 }
|
Chris@16
|
349
|
Chris@16
|
350
|
Chris@101
|
351 template <typename OutputIterator>
|
Chris@101
|
352 static inline void output_ranges(container_type const& first, container_type const& second,
|
Chris@101
|
353 OutputIterator out, bool closed)
|
Chris@16
|
354 {
|
Chris@101
|
355 std::copy(boost::begin(first), boost::end(first), out);
|
Chris@101
|
356
|
Chris@101
|
357 BOOST_ASSERT(closed ? !boost::empty(second) : boost::size(second) > 1);
|
Chris@101
|
358 std::copy(++boost::rbegin(second), // skip the first Point
|
Chris@101
|
359 closed ? boost::rend(second) : --boost::rend(second), // skip the last Point if open
|
Chris@101
|
360 out);
|
Chris@101
|
361
|
Chris@101
|
362 typedef typename boost::range_size<container_type>::type size_type;
|
Chris@101
|
363 size_type const count = boost::size(first) + boost::size(second) - 1;
|
Chris@101
|
364 // count describes a closed case but comparison with min size of closed
|
Chris@101
|
365 // gives the result compatible also with open
|
Chris@101
|
366 // here core_detail::closure::minimum_ring_size<closed> could be used
|
Chris@101
|
367 if (count < 4)
|
Chris@16
|
368 {
|
Chris@101
|
369 // there should be only one missing
|
Chris@101
|
370 *out++ = *boost::begin(first);
|
Chris@16
|
371 }
|
Chris@16
|
372 }
|
Chris@16
|
373 };
|
Chris@16
|
374
|
Chris@16
|
375 }} // namespace strategy::convex_hull
|
Chris@16
|
376
|
Chris@16
|
377
|
Chris@16
|
378 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
|
Chris@16
|
379 template <typename InputGeometry, typename OutputPoint>
|
Chris@16
|
380 struct strategy_convex_hull<InputGeometry, OutputPoint, cartesian_tag>
|
Chris@16
|
381 {
|
Chris@16
|
382 typedef strategy::convex_hull::graham_andrew<InputGeometry, OutputPoint> type;
|
Chris@16
|
383 };
|
Chris@16
|
384 #endif
|
Chris@16
|
385
|
Chris@16
|
386 }} // namespace boost::geometry
|
Chris@16
|
387
|
Chris@16
|
388
|
Chris@16
|
389 #endif // BOOST_GEOMETRY_STRATEGIES_AGNOSTIC_CONVEX_GRAHAM_ANDREW_HPP
|