@@ -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,14 +423,80 @@ struct touch : public base_turn_handler
369423 return side1 == side2 && ! opposite (side1, turn);
370424 }
371425
372- /* static inline void block_second(bool block, TurnInfo& ti)
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)
373434 {
374- if (block)
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 )
375470 {
376- ti.operations[1].operation = operation_blocked;
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 ;
377480 }
378- }*/
379481
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
380500
381501 template
382502 <
@@ -400,9 +520,10 @@ struct touch : public base_turn_handler
400520 bool const has_pk = ! range_p.is_last_segment ();
401521 bool const has_qk = ! range_q.is_last_segment ();
402522
403- int const side_qi_p1 = dir_info.sides .template get <1 , 0 >();
404- 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 ;
405524
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 ;
406527
407528 // If Qi and Qk are both at same side of Pi-Pj,
408529 // or collinear (so: not opposite sides)
@@ -413,6 +534,7 @@ struct touch : public base_turn_handler
413534 int const side_qk_q = has_qk ? side.qk_wrt_q1 () : 0 ;
414535
415536 bool const q_turns_left = side_qk_q == 1 ;
537+
416538 bool const block_q = side_qk_p1 == 0
417539 && ! same (side_qi_p1, side_qk_q)
418540 ;
@@ -422,9 +544,18 @@ struct touch : public base_turn_handler
422544 // or Q is fully collinear && P turns not to left
423545 if (side_pk_p == side_qi_p1
424546 || side_pk_p == side_qk_p1
425- || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1 )
426- )
547+ || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1 ))
427548 {
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
428559 // Collinear -> lines join, continue
429560 // (#BRL2)
430561 if (side_pk_q2 == 0 && ! block_q)
@@ -433,8 +564,6 @@ struct touch : public base_turn_handler
433564 return ;
434565 }
435566
436- int const side_pk_q1 = has_pk && has_qk ? side.pk_wrt_q1 () : 0 ;
437-
438567 // Collinear opposite case -> block P
439568 // (#BRL4, #BLR8)
440569 if (side_pk_q1 == 0 )
@@ -457,7 +586,6 @@ struct touch : public base_turn_handler
457586 {
458587 ti.operations [1 ].operation = operation_blocked;
459588 }
460- // block_second(block_q, ti);
461589 return ;
462590 }
463591
@@ -483,7 +611,6 @@ struct touch : public base_turn_handler
483611 {
484612 ti.touch_only = true ;
485613 }
486- // block_second(block_q, ti);
487614 return ;
488615 }
489616 }
@@ -508,8 +635,9 @@ struct touch : public base_turn_handler
508635 }
509636 else
510637 {
638+ // The qi/qk are opposite to each other, w.r.t. p1
511639 // From left to right or from right to left
512- 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 ;
513641 bool const right_to_left = side_qk_p1 == 1 ;
514642
515643 // If p turns into direction of qi (1,2)
@@ -601,23 +729,23 @@ struct equal : public base_turn_handler
601729 // Without rescaling, to check for union/intersection,
602730 // try to check side values (without any thresholds)
603731 typedef typename select_coordinate_type
604- <
605- typename UniqueSubRange1::point_type,
606- typename UniqueSubRange2::point_type
607- >::type coordinate_type;
732+ <
733+ typename UniqueSubRange1::point_type,
734+ typename UniqueSubRange2::point_type
735+ >::type coordinate_type;
608736
609737 typedef detail::distance_measure<coordinate_type> dm_type;
610738
611- dm_type const dm_qk_p
739+ dm_type const dm_pk_q2
612740 = get_distance_measure (range_q.at (1 ), range_q.at (2 ), range_p.at (2 ));
613- dm_type const dm_pk_q
741+ dm_type const dm_qk_p2
614742 = get_distance_measure (range_p.at (1 ), range_p.at (2 ), range_q.at (2 ));
615743
616- if (dm_pk_q .measure != dm_qk_p .measure )
744+ if (dm_qk_p2 .measure != dm_pk_q2 .measure )
617745 {
618746 // A (possibly very small) difference is detected, which
619747 // can be used to distinguish between union/intersection
620- ui_else_iu (dm_pk_q .measure < dm_qk_p .measure , ti);
748+ ui_else_iu (dm_qk_p2 .measure < dm_pk_q2 .measure , ti);
621749 return ;
622750 }
623751 }
0 commit comments