@@ -186,7 +186,8 @@ struct base_turn_handler
186186 {
187187 ti.method = method;
188188
189- // For touch/touch interior always take the intersection point 0 (there is only one).
189+ // For touch/touch interior always take the intersection point 0
190+ // (usually there is only one - but if collinear is handled as touch, both could be taken).
190191 static int const index = 0 ;
191192
192193 geometry::convert (info.intersections [index], ti.point );
@@ -965,8 +966,57 @@ template
965966>
966967struct collinear : public base_turn_handler
967968{
969+ template
970+ <
971+ typename IntersectionInfo,
972+ typename UniqueSubRange1,
973+ typename UniqueSubRange2,
974+ typename DirInfo
975+ >
976+ static bool handle_as_equal (IntersectionInfo const & info,
977+ UniqueSubRange1 const & range_p,
978+ UniqueSubRange2 const & range_q,
979+ DirInfo const & dir_info)
980+ {
981+ #if defined(BOOST_GEOMETRY_USE_RESCALING)
982+ return false ;
983+ #endif
984+ int const arrival_p = dir_info.arrival [0 ];
985+ int const arrival_q = dir_info.arrival [1 ];
986+ if (arrival_p * arrival_q != -1 || info.count != 2 )
987+ {
988+ // Code below assumes that either p or q arrives in the other segment
989+ return false ;
990+ }
991+
992+ auto const location = distance_measure (info.intersections [1 ],
993+ arrival_p == 1 ? range_q.at (1 ) : range_p.at (1 ));
994+ decltype (location) const zero = 0 ;
995+ return math::equals (location, zero);
996+ }
997+
968998 /*
969- arrival P pk//p1 qk//q1 product* case result
999+ Either P arrives within Q (arrival_p == -1) or Q arrives within P.
1000+
1001+ Typical situation:
1002+ ^q2
1003+ /
1004+ ^q1
1005+ / ____ ip[1]
1006+ //|p1 } this section of p/q is colllinear
1007+ q0// | } ____ ip[0]
1008+ / |
1009+ / v
1010+ p0 p2
1011+
1012+ P arrives (at p1) in segment Q (between q0 and q1).
1013+ Therefore arrival_p == 1
1014+ P (p2) goes to the right (-1). Follow P for intersection, or follow Q for union.
1015+ Therefore if (arrival_p==1) and side_p==-1, result = iu
1016+
1017+ Complete table:
1018+
1019+ arrival P pk//p1 qk//q1 product case result
9701020 1 1 1 CLL1 ui
9711021 -1 1 -1 CLL2 iu
9721022 1 1 1 CLR1 ui
@@ -980,7 +1030,9 @@ struct collinear : public base_turn_handler
9801030 1 0 0 CC1 cc
9811031 -1 0 0 CC2 cc
9821032
983- *product = arrival * (pk//p1 or qk//q1)
1033+ Resulting in the rule:
1034+ The arrival-info multiplied by the relevant side delivers the result.
1035+ product = arrival * (pk//p1 or qk//q1)
9841036
9851037 Stated otherwise:
9861038 - if P arrives: look at turn P
@@ -989,13 +1041,6 @@ struct collinear : public base_turn_handler
9891041 - if P arrives and P turns right: intersection for P
9901042 - if Q arrives and Q turns left: union for Q (=intersection for P)
9911043 - if Q arrives and Q turns right: intersection for Q (=union for P)
992-
993- ROBUSTNESS: p and q are collinear, so you would expect
994- that side qk//p1 == pk//q1. But that is not always the case
995- in near-epsilon ranges. Then decision logic is different.
996- If p arrives, q is further, so the angle qk//p1 is (normally)
997- more precise than pk//p1
998-
9991044 */
10001045 template
10011046 <
@@ -1016,29 +1061,25 @@ struct collinear : public base_turn_handler
10161061 // Copy the intersection point in TO direction
10171062 assign_point (ti, method_collinear, info, non_opposite_to_index (info));
10181063
1019- int const arrival = dir_info.arrival [0 ];
1064+ int const arrival_p = dir_info.arrival [0 ];
10201065 // Should not be 0, this is checked before
1021- BOOST_GEOMETRY_ASSERT (arrival != 0 );
1066+ BOOST_GEOMETRY_ASSERT (arrival_p != 0 );
10221067
10231068 bool const has_pk = ! range_p.is_last_segment ();
10241069 bool const has_qk = ! range_q.is_last_segment ();
10251070 int const side_p = has_pk ? side.pk_wrt_p1 () : 0 ;
10261071 int const side_q = has_qk ? side.qk_wrt_q1 () : 0 ;
10271072
10281073 // If p arrives, use p, else use q
1029- int const side_p_or_q = arrival == 1
1074+ int const side_p_or_q = arrival_p == 1
10301075 ? side_p
10311076 : side_q
10321077 ;
10331078
1034- // See comments above,
1035- // resulting in a strange sort of mathematic rule here:
1036- // The arrival-info multiplied by the relevant side
1037- // delivers a consistent result.
1079+ // Calculate product according to comments above.
1080+ int const product = arrival_p * side_p_or_q;
10381081
1039- int const product = arrival * side_p_or_q;
1040-
1041- if (product == 0 )
1082+ if (product == 0 )
10421083 {
10431084 both (ti, operation_continue);
10441085 }
@@ -1186,11 +1227,11 @@ private :
11861227 {
11871228 TurnInfo tp = tp_model;
11881229
1189- int const p_arrival = info.d_info ().arrival [0 ];
1190- int const q_arrival = info.d_info ().arrival [1 ];
1230+ int const arrival_p = info.d_info ().arrival [0 ];
1231+ int const arrival_q = info.d_info ().arrival [1 ];
11911232
11921233 // If P arrives within Q, there is a turn dependent on P
1193- if ( p_arrival == 1
1234+ if ( arrival_p == 1
11941235 && ! range_p.is_last_segment ()
11951236 && set_tp<0 >(side.pk_wrt_p1 (), tp, info.i_info ()) )
11961237 {
@@ -1200,7 +1241,7 @@ private :
12001241 }
12011242
12021243 // If Q arrives within P, there is a turn dependent on Q
1203- if ( q_arrival == 1
1244+ if ( arrival_q == 1
12041245 && ! range_q.is_last_segment ()
12051246 && set_tp<1 >(side.qk_wrt_q1 (), tp, info.i_info ()) )
12061247 {
@@ -1212,8 +1253,8 @@ private :
12121253 if (AssignPolicy::include_opposite)
12131254 {
12141255 // Handle cases not yet handled above
1215- if ((q_arrival == -1 && p_arrival == 0 )
1216- || (p_arrival == -1 && q_arrival == 0 ))
1256+ if ((arrival_q == -1 && arrival_p == 0 )
1257+ || (arrival_p == -1 && arrival_q == 0 ))
12171258 {
12181259 for (unsigned int i = 0 ; i < 2 ; i++)
12191260 {
@@ -1420,9 +1461,10 @@ struct get_turn_info
14201461 // Collinear
14211462 if ( ! inters.d_info ().opposite )
14221463 {
1423-
1424- if ( inters.d_info (). arrival [ 0 ] == 0 )
1464+ if (inters. d_info (). arrival [ 0 ] == 0
1465+ || collinear<TurnInfo>:: handle_as_equal ( inters.i_info (), range_p, range_q, inters. d_info ()) )
14251466 {
1467+ // Both segments arrive at the second intersection point
14261468 handle_as_equal = true ;
14271469 }
14281470 else
0 commit comments