Skip to content

Commit 18f6ad3

Browse files
Merge pull request #790 from barendgehrels/fix/get-turn-info-collinear
Fix/get turn info collinear
2 parents 705efe9 + d006d1e commit 18f6ad3

File tree

3 files changed

+135
-90
lines changed

3 files changed

+135
-90
lines changed

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

Lines changed: 85 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP
1616
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP
1717

18-
1918
#include <boost/core/ignore_unused.hpp>
2019
#include <boost/throw_exception.hpp>
2120

@@ -28,9 +27,6 @@
2827
#include <boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp>
2928
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
3029

31-
#include <boost/geometry/geometries/segment.hpp>
32-
33-
#include <boost/geometry/policies/robustness/robust_point_type.hpp>
3430
#include <boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp>
3531

3632
// Silence warning C4127: conditional expression is constant
@@ -131,15 +127,7 @@ struct base_turn_handler
131127
return 0;
132128
}
133129

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));
130+
auto const dm = get_distance_measure(range_p.at(range_index), range_p.at(range_index + 1), range_q.at(point_index));
143131
return dm.measure == 0 ? 0 : dm.measure > 0 ? 1 : -1;
144132
}
145133

@@ -186,7 +174,8 @@ struct base_turn_handler
186174
{
187175
ti.method = method;
188176

189-
// For touch/touch interior always take the intersection point 0 (there is only one).
177+
// For touch/touch interior always take the intersection point 0
178+
// (usually there is only one - but if collinear is handled as touch, both could be taken).
190179
static int const index = 0;
191180

192181
geometry::convert(info.intersections[index], ti.point);
@@ -225,9 +214,8 @@ struct base_turn_handler
225214
{
226215
// TODO: use comparable distance for point-point instead - but that
227216
// causes currently cycling include problems
228-
typedef typename geometry::coordinate_type<Point1>::type ctype;
229-
ctype const dx = get<0>(a) - get<0>(b);
230-
ctype const dy = get<1>(a) - get<1>(b);
217+
auto const dx = get<0>(a) - get<0>(b);
218+
auto const dy = get<1>(a) - get<1>(b);
231219
return dx * dx + dy * dy;
232220
}
233221

@@ -259,19 +247,10 @@ struct base_turn_handler
259247
// pk/q2 is considered as collinear, but there might be
260248
// a tiny measurable difference. If so, use that.
261249
// Calculate pk // qj-qk
262-
typedef detail::distance_measure
263-
<
264-
typename select_coordinate_type
265-
<
266-
typename UniqueSubRange1::point_type,
267-
typename UniqueSubRange2::point_type
268-
>::type
269-
> dm_type;
270-
271-
const bool p_closer =
250+
bool const p_closer =
272251
ti.operations[IndexP].remaining_distance
273252
< ti.operations[IndexQ].remaining_distance;
274-
dm_type const dm
253+
auto const dm
275254
= p_closer
276255
? get_distance_measure(range_q.at(index_q - 1),
277256
range_q.at(index_q), range_p.at(index_p))
@@ -282,8 +261,7 @@ struct base_turn_handler
282261
{
283262
// Not truely collinear, distinguish for union/intersection
284263
// If p goes left (positive), take that for a union
285-
286-
bool p_left = p_closer ? dm.is_positive() : dm.is_negative();
264+
bool const p_left = p_closer ? dm.is_positive() : dm.is_negative();
287265

288266
ti.operations[IndexP].operation = p_left
289267
? operation_union : operation_intersection;
@@ -347,14 +325,9 @@ struct touch_interior : public base_turn_handler
347325
// Therefore handle it as a normal touch (two segments arrive at the
348326
// intersection point). It currently checks for zero, but even a
349327
// distance a little bit larger would do.
350-
typedef typename geometry::coordinate_type
351-
<
352-
typename UniqueSubRange::point_type
353-
>::type coor_t;
354-
355-
coor_t const location = distance_measure(info.intersections[0], non_touching_range.at(1));
356-
coor_t const zero = 0;
357-
bool const result = math::equals(location, zero);
328+
auto const dm = distance_measure(info.intersections[0], non_touching_range.at(1));
329+
decltype(dm) const zero = 0;
330+
bool const result = math::equals(dm, zero);
358331
return result;
359332
}
360333

@@ -540,16 +513,8 @@ struct touch : public base_turn_handler
540513
// >----->P qj is LEFT of P1 and pi is LEFT of Q2
541514
// (the other way round is also possible)
542515

543-
typedef typename select_coordinate_type
544-
<
545-
typename UniqueSubRange1::point_type,
546-
typename UniqueSubRange2::point_type
547-
>::type coordinate_type;
548-
549-
typedef detail::distance_measure<coordinate_type> dm_type;
550-
551-
dm_type const dm_qj_p1 = get_distance_measure(range_p.at(0), range_p.at(1), range_q.at(1));
552-
dm_type const dm_pi_q2 = get_distance_measure(range_q.at(1), range_q.at(2), range_p.at(0));
516+
auto const dm_qj_p1 = get_distance_measure(range_p.at(0), range_p.at(1), range_q.at(1));
517+
auto const dm_pi_q2 = get_distance_measure(range_q.at(1), range_q.at(2), range_p.at(0));
553518

554519
if (dm_qj_p1.measure > 0 && dm_pi_q2.measure > 0)
555520
{
@@ -564,8 +529,8 @@ struct touch : public base_turn_handler
564529
return true;
565530
}
566531

567-
dm_type const dm_pj_q1 = get_distance_measure(range_q.at(0), range_q.at(1), range_p.at(1));
568-
dm_type const dm_qi_p2 = get_distance_measure(range_p.at(1), range_p.at(2), range_q.at(0));
532+
auto const dm_pj_q1 = get_distance_measure(range_q.at(0), range_q.at(1), range_p.at(1));
533+
auto const dm_qi_p2 = get_distance_measure(range_p.at(1), range_p.at(2), range_q.at(0));
569534

570535
if (dm_pj_q1.measure > 0 && dm_qi_p2.measure > 0)
571536
{
@@ -813,17 +778,9 @@ struct equal : public base_turn_handler
813778
// They turn to the same side, or continue both collinearly
814779
// Without rescaling, to check for union/intersection,
815780
// try to check side values (without any thresholds)
816-
typedef typename select_coordinate_type
817-
<
818-
typename UniqueSubRange1::point_type,
819-
typename UniqueSubRange2::point_type
820-
>::type coordinate_type;
821-
822-
typedef detail::distance_measure<coordinate_type> dm_type;
823-
824-
dm_type const dm_pk_q2
781+
auto const dm_pk_q2
825782
= get_distance_measure(range_q.at(1), range_q.at(2), range_p.at(2));
826-
dm_type const dm_qk_p2
783+
auto const dm_qk_p2
827784
= get_distance_measure(range_p.at(1), range_p.at(2), range_q.at(2));
828785

829786
if (dm_qk_p2.measure != dm_pk_q2.measure)
@@ -965,8 +922,57 @@ template
965922
>
966923
struct collinear : public base_turn_handler
967924
{
925+
template
926+
<
927+
typename IntersectionInfo,
928+
typename UniqueSubRange1,
929+
typename UniqueSubRange2,
930+
typename DirInfo
931+
>
932+
static bool handle_as_equal(IntersectionInfo const& info,
933+
UniqueSubRange1 const& range_p,
934+
UniqueSubRange2 const& range_q,
935+
DirInfo const& dir_info)
936+
{
937+
#if defined(BOOST_GEOMETRY_USE_RESCALING)
938+
return false;
939+
#endif
940+
int const arrival_p = dir_info.arrival[0];
941+
int const arrival_q = dir_info.arrival[1];
942+
if (arrival_p * arrival_q != -1 || info.count != 2)
943+
{
944+
// Code below assumes that either p or q arrives in the other segment
945+
return false;
946+
}
947+
948+
auto const dm = distance_measure(info.intersections[1],
949+
arrival_p == 1 ? range_q.at(1) : range_p.at(1));
950+
decltype(dm) const zero = 0;
951+
return math::equals(dm, zero);
952+
}
953+
968954
/*
969-
arrival P pk//p1 qk//q1 product* case result
955+
Either P arrives within Q (arrival_p == -1) or Q arrives within P.
956+
957+
Typical situation:
958+
^q2
959+
/
960+
^q1
961+
/ ____ ip[1]
962+
//|p1 } this section of p/q is colllinear
963+
q0// | } ____ ip[0]
964+
/ |
965+
/ v
966+
p0 p2
967+
968+
P arrives (at p1) in segment Q (between q0 and q1).
969+
Therefore arrival_p == 1
970+
P (p2) goes to the right (-1). Follow P for intersection, or follow Q for union.
971+
Therefore if (arrival_p==1) and side_p==-1, result = iu
972+
973+
Complete table:
974+
975+
arrival P pk//p1 qk//q1 product case result
970976
1 1 1 CLL1 ui
971977
-1 1 -1 CLL2 iu
972978
1 1 1 CLR1 ui
@@ -980,7 +986,9 @@ struct collinear : public base_turn_handler
980986
1 0 0 CC1 cc
981987
-1 0 0 CC2 cc
982988
983-
*product = arrival * (pk//p1 or qk//q1)
989+
Resulting in the rule:
990+
The arrival-info multiplied by the relevant side delivers the result.
991+
product = arrival * (pk//p1 or qk//q1)
984992
985993
Stated otherwise:
986994
- if P arrives: look at turn P
@@ -989,13 +997,6 @@ struct collinear : public base_turn_handler
989997
- if P arrives and P turns right: intersection for P
990998
- if Q arrives and Q turns left: union for Q (=intersection for P)
991999
- 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-
9991000
*/
10001001
template
10011002
<
@@ -1016,29 +1017,25 @@ struct collinear : public base_turn_handler
10161017
// Copy the intersection point in TO direction
10171018
assign_point(ti, method_collinear, info, non_opposite_to_index(info));
10181019

1019-
int const arrival = dir_info.arrival[0];
1020+
int const arrival_p = dir_info.arrival[0];
10201021
// Should not be 0, this is checked before
1021-
BOOST_GEOMETRY_ASSERT(arrival != 0);
1022+
BOOST_GEOMETRY_ASSERT(arrival_p != 0);
10221023

10231024
bool const has_pk = ! range_p.is_last_segment();
10241025
bool const has_qk = ! range_q.is_last_segment();
10251026
int const side_p = has_pk ? side.pk_wrt_p1() : 0;
10261027
int const side_q = has_qk ? side.qk_wrt_q1() : 0;
10271028

10281029
// If p arrives, use p, else use q
1029-
int const side_p_or_q = arrival == 1
1030+
int const side_p_or_q = arrival_p == 1
10301031
? side_p
10311032
: side_q
10321033
;
10331034

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.
1035+
// Calculate product according to comments above.
1036+
int const product = arrival_p * side_p_or_q;
10381037

1039-
int const product = arrival * side_p_or_q;
1040-
1041-
if(product == 0)
1038+
if (product == 0)
10421039
{
10431040
both(ti, operation_continue);
10441041
}
@@ -1186,11 +1183,11 @@ private :
11861183
{
11871184
TurnInfo tp = tp_model;
11881185

1189-
int const p_arrival = info.d_info().arrival[0];
1190-
int const q_arrival = info.d_info().arrival[1];
1186+
int const arrival_p = info.d_info().arrival[0];
1187+
int const arrival_q = info.d_info().arrival[1];
11911188

11921189
// If P arrives within Q, there is a turn dependent on P
1193-
if ( p_arrival == 1
1190+
if ( arrival_p == 1
11941191
&& ! range_p.is_last_segment()
11951192
&& set_tp<0>(side.pk_wrt_p1(), tp, info.i_info()) )
11961193
{
@@ -1200,7 +1197,7 @@ private :
12001197
}
12011198

12021199
// If Q arrives within P, there is a turn dependent on Q
1203-
if ( q_arrival == 1
1200+
if ( arrival_q == 1
12041201
&& ! range_q.is_last_segment()
12051202
&& set_tp<1>(side.qk_wrt_q1(), tp, info.i_info()) )
12061203
{
@@ -1212,8 +1209,8 @@ private :
12121209
if (AssignPolicy::include_opposite)
12131210
{
12141211
// Handle cases not yet handled above
1215-
if ((q_arrival == -1 && p_arrival == 0)
1216-
|| (p_arrival == -1 && q_arrival == 0))
1212+
if ((arrival_q == -1 && arrival_p == 0)
1213+
|| (arrival_p == -1 && arrival_q == 0))
12171214
{
12181215
for (unsigned int i = 0; i < 2; i++)
12191216
{
@@ -1420,9 +1417,10 @@ struct get_turn_info
14201417
// Collinear
14211418
if ( ! inters.d_info().opposite )
14221419
{
1423-
1424-
if ( inters.d_info().arrival[0] == 0 )
1420+
if (inters.d_info().arrival[0] == 0
1421+
|| collinear<TurnInfo>::handle_as_equal(inters.i_info(), range_p, range_q, inters.d_info()))
14251422
{
1423+
// Both segments arrive at the second intersection point
14261424
handle_as_equal = true;
14271425
}
14281426
else

0 commit comments

Comments
 (0)