@@ -111,6 +111,60 @@ struct base_turn_handler
111111 both (ti, condition ? operation_union : operation_intersection);
112112 }
113113
114+
115+ #if ! defined(BOOST_GEOMETRY_USE_RESCALING)
116+ template
117+ <
118+ typename UniqueSubRange1,
119+ typename UniqueSubRange2
120+ >
121+ static inline int side_with_distance_measure (UniqueSubRange1 const & range_p,
122+ UniqueSubRange2 const & range_q,
123+ int range_index, int point_index)
124+ {
125+ if (range_index >= 1 && range_p.is_last_segment ())
126+ {
127+ return 0 ;
128+ }
129+ if (point_index >= 2 && range_q.is_last_segment ())
130+ {
131+ return 0 ;
132+ }
133+
134+ typedef typename select_coordinate_type
135+ <
136+ typename UniqueSubRange1::point_type,
137+ typename UniqueSubRange2::point_type
138+ >::type coordinate_type;
139+
140+ typedef detail::distance_measure<coordinate_type> dm_type;
141+
142+ dm_type const dm = get_distance_measure (range_p.at (range_index), range_p.at (range_index + 1 ), range_q.at (point_index));
143+ return dm.measure == 0 ? 0 : dm.measure > 0 ? 1 : -1 ;
144+ }
145+
146+ template
147+ <
148+ typename UniqueSubRange1,
149+ typename UniqueSubRange2
150+ >
151+ static inline int verified_side (int side,
152+ UniqueSubRange1 const & range_p,
153+ UniqueSubRange2 const & range_q,
154+ int range_index,
155+ int point_index)
156+ {
157+ return side == 0 ? side_with_distance_measure (range_p, range_q, range_index, point_index) : side;
158+ }
159+ #else
160+ template <typename T1, typename T2>
161+ static inline int verified_side (int side, T1 const & , T2 const & , int , int )
162+ {
163+ return side;
164+ }
165+ #endif
166+
167+
114168 template <typename TurnInfo, typename IntersectionInfo>
115169 static inline void assign_point (TurnInfo& ti,
116170 method_type method,
@@ -178,7 +232,7 @@ struct base_turn_handler
178232 <
179233 typename UniqueSubRange1::point_type,
180234 typename UniqueSubRange2::point_type
181- >::type
235+ >::type
182236 > dm_type;
183237
184238 const bool p_closer =
@@ -369,6 +423,81 @@ struct touch : public base_turn_handler
369423 return side1 == side2 && ! opposite (side1, turn);
370424 }
371425
426+ #if ! defined(BOOST_GEOMETRY_USE_RESCALING)
427+ template
428+ <
429+ typename UniqueSubRange1,
430+ typename UniqueSubRange2
431+ >
432+ static inline bool handle_imperfect_touch (UniqueSubRange1 const & range_p,
433+ UniqueSubRange2 const & range_q, TurnInfo& ti)
434+ {
435+ // Q
436+ // ^
437+ // ||
438+ // ||
439+ // |^----
440+ // >----->P
441+ // * * they touch here (P/Q are (nearly) on top)
442+ //
443+ // Q continues from where P comes.
444+ // P continues from where Q comes
445+ // This is often a blocking situation,
446+ // unless there are FP issues: there might be a distance
447+ // between Pj and Qj, in that case handle it as a union.
448+ //
449+ // Exaggerated:
450+ // Q
451+ // ^ Q is nearly vertical
452+ // \ but not completely - and still ends above P
453+ // | \qj In this case it should block P and
454+ // | ^------ set Q to Union
455+ // >----->P qj is LEFT of P1 and pi is LEFT of Q2
456+ // (the other way round is also possible)
457+
458+ typedef typename select_coordinate_type
459+ <
460+ typename UniqueSubRange1::point_type,
461+ typename UniqueSubRange2::point_type
462+ >::type coordinate_type;
463+
464+ typedef detail::distance_measure<coordinate_type> dm_type;
465+
466+ dm_type const dm_qj_p1 = get_distance_measure (range_p.at (0 ), range_p.at (1 ), range_q.at (1 ));
467+ dm_type const dm_pi_q2 = get_distance_measure (range_q.at (1 ), range_q.at (2 ), range_p.at (0 ));
468+
469+ if (dm_qj_p1.measure > 0 && dm_pi_q2.measure > 0 )
470+ {
471+ // Even though there is a touch, Q(j) is left of P1
472+ // and P(i) is still left from Q2.
473+ // It can continue.
474+ ti.operations [0 ].operation = operation_blocked;
475+ // Q turns right -> union (both independent),
476+ // Q turns left -> intersection
477+ ti.operations [1 ].operation = operation_union;
478+ ti.touch_only = true ;
479+ return true ;
480+ }
481+
482+ dm_type const dm_pj_q1 = get_distance_measure (range_q.at (0 ), range_q.at (1 ), range_p.at (1 ));
483+ dm_type const dm_qi_p2 = get_distance_measure (range_p.at (1 ), range_p.at (2 ), range_q.at (0 ));
484+
485+ if (dm_pj_q1.measure > 0 && dm_qi_p2.measure > 0 )
486+ {
487+ // Even though there is a touch, Q(j) is left of P1
488+ // and P(i) is still left from Q2.
489+ // It can continue.
490+ ti.operations [0 ].operation = operation_union;
491+ // Q turns right -> union (both independent),
492+ // Q turns left -> intersection
493+ ti.operations [1 ].operation = operation_blocked;
494+ ti.touch_only = true ;
495+ return true ;
496+ }
497+ return false ;
498+ }
499+ #endif
500+
372501 template
373502 <
374503 typename UniqueSubRange1,
@@ -391,9 +520,10 @@ struct touch : public base_turn_handler
391520 bool const has_pk = ! range_p.is_last_segment ();
392521 bool const has_qk = ! range_q.is_last_segment ();
393522
394- int const side_qi_p1 = dir_info.sides .template get <1 , 0 >();
395- int const side_qk_p1 = has_qk ? side.qk_wrt_p1 () : 0 ;
523+ int const side_pk_q1 = has_pk ? side.pk_wrt_q1 () : 0 ;
396524
525+ int const side_qi_p1 = verified_side (dir_info.sides .template get <1 , 0 >(), range_p, range_q, 0 , 0 );
526+ int const side_qk_p1 = has_qk ? verified_side (side.qk_wrt_p1 (), range_p, range_q, 0 , 2 ) : 0 ;
397527
398528 // If Qi and Qk are both at same side of Pi-Pj,
399529 // or collinear (so: not opposite sides)
@@ -404,6 +534,7 @@ struct touch : public base_turn_handler
404534 int const side_qk_q = has_qk ? side.qk_wrt_q1 () : 0 ;
405535
406536 bool const q_turns_left = side_qk_q == 1 ;
537+
407538 bool const block_q = side_qk_p1 == 0
408539 && ! same (side_qi_p1, side_qk_q)
409540 ;
@@ -413,9 +544,18 @@ struct touch : public base_turn_handler
413544 // or Q is fully collinear && P turns not to left
414545 if (side_pk_p == side_qi_p1
415546 || side_pk_p == side_qk_p1
416- || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1 )
417- )
547+ || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1 ))
418548 {
549+ #if ! defined(BOOST_GEOMETRY_USE_RESCALING)
550+ if (side_qk_p1 == 0 && side_pk_q1 == 0
551+ && has_qk && has_qk
552+ && handle_imperfect_touch (range_p, range_q, ti))
553+ {
554+ // If q continues collinearly (opposite) with p, it should be blocked
555+ // but (FP) not if there is just a tiny space in between
556+ return ;
557+ }
558+ #endif
419559 // Collinear -> lines join, continue
420560 // (#BRL2)
421561 if (side_pk_q2 == 0 && ! block_q)
@@ -424,8 +564,6 @@ struct touch : public base_turn_handler
424564 return ;
425565 }
426566
427- int const side_pk_q1 = has_pk && has_qk ? side.pk_wrt_q1 () : 0 ;
428-
429567 // Collinear opposite case -> block P
430568 // (#BRL4, #BLR8)
431569 if (side_pk_q1 == 0 )
@@ -497,8 +635,9 @@ struct touch : public base_turn_handler
497635 }
498636 else
499637 {
638+ // The qi/qk are opposite to each other, w.r.t. p1
500639 // From left to right or from right to left
501- int const side_pk_p = has_pk ? side.pk_wrt_p1 () : 0 ;
640+ int const side_pk_p = has_pk ? verified_side ( side.pk_wrt_p1 (), range_p, range_p, 0 , 2 ) : 0 ;
502641 bool const right_to_left = side_qk_p1 == 1 ;
503642
504643 // If p turns into direction of qi (1,2)
@@ -590,10 +729,10 @@ struct equal : public base_turn_handler
590729 // Without rescaling, to check for union/intersection,
591730 // try to check side values (without any thresholds)
592731 typedef typename select_coordinate_type
593- <
594- typename UniqueSubRange1::point_type,
595- typename UniqueSubRange2::point_type
596- >::type coordinate_type;
732+ <
733+ typename UniqueSubRange1::point_type,
734+ typename UniqueSubRange2::point_type
735+ >::type coordinate_type;
597736
598737 typedef detail::distance_measure<coordinate_type> dm_type;
599738
0 commit comments