2525#include < boost/geometry/algorithms/detail/overlay/turn_info.hpp>
2626
2727#include < boost/geometry/util/condition.hpp>
28+ #include < boost/geometry/util/math.hpp>
29+ #include < boost/geometry/util/select_coordinate_type.hpp>
30+ #include < boost/geometry/util/select_most_precise.hpp>
2831
2932namespace boost { namespace geometry
3033{
@@ -66,6 +69,8 @@ struct ranked_point
6669 , seg_id(si)
6770 {}
6871
72+ using point_type = Point;
73+
6974 Point point;
7075 rank_type rank;
7176 signed_size_type zone; // index of closed zone, in uu turn there would be 2 zones
@@ -252,59 +257,119 @@ public :
252257 , m_strategy(strategy)
253258 {}
254259
260+ template <typename Operation>
255261 void add_segment_from (signed_size_type turn_index, int op_index,
256262 Point const & point_from,
257- operation_type op, segment_identifier const & si ,
263+ Operation const & op ,
258264 bool is_origin)
259265 {
260- m_ranked_points.push_back (rp (point_from, turn_index, op_index, dir_from, op, si));
266+ m_ranked_points.push_back (rp (point_from, turn_index, op_index,
267+ dir_from, op.operation , op.seg_id ));
261268 if (is_origin)
262269 {
263270 m_origin = point_from;
264271 m_origin_count++;
265272 }
266273 }
267274
275+ template <typename Operation>
268276 void add_segment_to (signed_size_type turn_index, int op_index,
269277 Point const & point_to,
270- operation_type op, segment_identifier const & si )
278+ Operation const & op )
271279 {
272- m_ranked_points.push_back (rp (point_to, turn_index, op_index, dir_to, op, si));
280+ m_ranked_points.push_back (rp (point_to, turn_index, op_index,
281+ dir_to, op.operation , op.seg_id ));
273282 }
274283
284+ template <typename Operation>
275285 void add_segment (signed_size_type turn_index, int op_index,
276286 Point const & point_from, Point const & point_to,
277- operation_type op, segment_identifier const & si,
278- bool is_origin)
287+ Operation const & op, bool is_origin)
288+ {
289+ add_segment_from (turn_index, op_index, point_from, op, is_origin);
290+ add_segment_to (turn_index, op_index, point_to, op);
291+ }
292+
293+ // Returns true if two points are approximately equal, tuned by a giga-epsilon constant
294+ // (if constant is 1.0, for type double, the boundary is about 1.0e-7)
295+ template <typename Point1, typename Point2, typename T>
296+ static inline bool approximately_equals (Point1 const & a, Point2 const & b,
297+ T const & limit_giga_epsilon)
279298 {
280- add_segment_from (turn_index, op_index, point_from, op, si, is_origin);
281- add_segment_to (turn_index, op_index, point_to, op, si);
299+ // Including distance would introduce cyclic dependencies.
300+ using coor_t = typename select_coordinate_type<Point1, Point2>::type;
301+ using calc_t = typename geometry::select_most_precise <coor_t , T>::type;
302+ constexpr calc_t machine_giga_epsilon = 1.0e9 * std::numeric_limits<calc_t >::epsilon ();
303+
304+ calc_t const & a0 = geometry::get<0 >(a);
305+ calc_t const & b0 = geometry::get<0 >(b);
306+ calc_t const & a1 = geometry::get<1 >(a);
307+ calc_t const & b1 = geometry::get<1 >(b);
308+ calc_t const one = 1.0 ;
309+ calc_t const c = math::detail::greatest (a0, b0, a1, b1, one);
310+
311+ // The maximum limit is avoid, for floating point, large limits like 400
312+ // (which are be calculated using eps)
313+ constexpr calc_t maxlimit = 1.0e-3 ;
314+ auto const limit = (std::min)(maxlimit, limit_giga_epsilon * machine_giga_epsilon * c);
315+ return std::abs (a0 - b0) <= limit && std::abs (a1 - b1) <= limit;
282316 }
283317
284318 template <typename Operation, typename Geometry1, typename Geometry2>
285- Point add (Operation const & op, signed_size_type turn_index, int op_index,
319+ static Point walk_over_ring (Operation const & op, int offset,
320+ Geometry1 const & geometry1,
321+ Geometry2 const & geometry2)
322+ {
323+ Point point;
324+ geometry::copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, op.seg_id , offset, point);
325+ return point;
326+ }
327+
328+ template <typename Turn, typename Operation, typename Geometry1, typename Geometry2>
329+ Point add (Turn const & turn, Operation const & op, signed_size_type turn_index, int op_index,
286330 Geometry1 const & geometry1,
287331 Geometry2 const & geometry2,
288332 bool is_origin)
289333 {
290- Point point1 , point2, point3;
334+ Point point_from , point2, point3;
291335 geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
292- op.seg_id , point1, point2, point3);
293- Point const & point_to = op.fraction .is_one () ? point3 : point2;
294- add_segment (turn_index, op_index, point1, point_to, op.operation , op.seg_id , is_origin);
295- return point1;
336+ op.seg_id , point_from, point2, point3);
337+ Point point_to = op.fraction .is_one () ? point3 : point2;
338+
339+
340+ // If the point is in the neighbourhood (the limit itself is not important),
341+ // then take a point (or more) further back.
342+ // The limit of offset avoids theoretical infinite loops. In practice it currently
343+ // walks max 1 point back in all cases.
344+ int offset = 0 ;
345+ while (approximately_equals (point_from, turn.point , 1.0 ) && offset > -10 )
346+ {
347+ point_from = walk_over_ring (op, --offset, geometry1, geometry2);
348+ }
349+
350+ // Similarly for the point to, walk forward
351+ offset = 0 ;
352+ while (approximately_equals (point_to, turn.point , 1.0 ) && offset < 10 )
353+ {
354+ point_to = walk_over_ring (op, ++offset, geometry1, geometry2);
355+ }
356+
357+ add_segment (turn_index, op_index, point_from, point_to, op, is_origin);
358+
359+ return point_from;
296360 }
297361
298- template <typename Operation, typename Geometry1, typename Geometry2>
299- void add (Operation const & op, signed_size_type turn_index, int op_index,
362+ template <typename Turn, typename Operation, typename Geometry1, typename Geometry2>
363+ void add (Turn const & turn,
364+ Operation const & op, signed_size_type turn_index, int op_index,
300365 segment_identifier const & departure_seg_id,
301366 Geometry1 const & geometry1,
302367 Geometry2 const & geometry2,
303- bool check_origin )
368+ bool is_departure )
304369 {
305- Point const point1 = add (op, turn_index, op_index, geometry1, geometry2, false );
370+ Point potential_origin = add (turn, op, turn_index, op_index, geometry1, geometry2, false );
306371
307- if (check_origin )
372+ if (is_departure )
308373 {
309374 bool const is_origin
310375 = op.seg_id .source_index == departure_seg_id.source_index
@@ -317,33 +382,41 @@ public :
317382 if (m_origin_count == 0 ||
318383 segment_distance < m_origin_segment_distance)
319384 {
320- m_origin = point1 ;
385+ m_origin = potential_origin ;
321386 m_origin_segment_distance = segment_distance;
322387 }
323388 m_origin_count++;
324389 }
325390 }
326391 }
327392
393+ template <typename Operation, typename Geometry1, typename Geometry2>
394+ static signed_size_type segment_count_on_ring (Operation const & op,
395+ Geometry1 const & geometry1,
396+ Geometry2 const & geometry2)
397+ {
398+ // Take wrap into account
399+ // Suppose point_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2,
400+ // then distance=9-7+2=4, being segments 7,8,0,1
401+ return op.seg_id .source_index == 0
402+ ? detail::overlay::segment_count_on_ring (geometry1, op.seg_id )
403+ : detail::overlay::segment_count_on_ring (geometry2, op.seg_id );
404+ }
405+
328406 template <typename Operation, typename Geometry1, typename Geometry2>
329407 static signed_size_type calculate_segment_distance (Operation const & op,
330408 segment_identifier const & departure_seg_id,
331409 Geometry1 const & geometry1,
332410 Geometry2 const & geometry2)
333411 {
412+ BOOST_ASSERT (op.seg_id .source_index == departure_seg_id.source_index );
413+ signed_size_type result = op.seg_id .segment_index - departure_seg_id.segment_index ;
334414 if (op.seg_id .segment_index >= departure_seg_id.segment_index )
335415 {
336416 // dep.seg_id=5, op.seg_id=7, distance=2, being segments 5,6
337- return op. seg_id . segment_index - departure_seg_id. segment_index ;
417+ return result ;
338418 }
339- // Take wrap into account
340- // Suppose point_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2,
341- // then distance=9-7+2=4, being segments 7,8,0,1
342- std::size_t const segment_count
343- = op.seg_id .source_index == 0
344- ? segment_count_on_ring (geometry1, op.seg_id )
345- : segment_count_on_ring (geometry2, op.seg_id );
346- return segment_count - departure_seg_id.segment_index + op.seg_id .segment_index ;
419+ return segment_count_on_ring (op, geometry1, geometry2) + result;
347420 }
348421
349422 void apply (Point const & turn_point)
0 commit comments