Skip to content

Commit 84d3a8d

Browse files
committed
[get_turn_info] handle collinear as equal if both segments arrive at intersection point
1 parent 705efe9 commit 84d3a8d

File tree

1 file changed

+70
-28
lines changed

1 file changed

+70
-28
lines changed

include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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
>
966967
struct 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

Comments
 (0)