Skip to content

Commit 0969372

Browse files
committed
[get_turns] use distance measures for side, and handle situation
with an imperfect touch (floating point issue)
1 parent 761170b commit 0969372

File tree

4 files changed

+158
-49
lines changed

4 files changed

+158
-49
lines changed

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

Lines changed: 151 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

test/algorithms/buffer/buffer_multi_polygon.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,6 @@ static std::string const mysql_report_2015_07_05_1
307307
static std::string const mysql_report_2015_07_05_2
308308
= "MULTIPOLYGON(((19777 -21893,3.22595e+307 6.86823e+307,-40 -13,19777 -21893)),((-1322 4851,8.49998e+307 3.94481e+307,75 -69,8.64636e+307 3.94909e+307,-1.15292e+18 7.20576e+16,-1322 4851)))";
309309

310-
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) \
311-
&& defined(BOOST_GEOMETRY_USE_KRAMER_RULE) \
312-
&& ! defined(BOOST_GEOMETRY_TEST_FAILURES)
313-
// These testcases are failing for non-rescaled Kramer rule
314-
#define BOOST_GEOMETRY_EXCLUDE
315-
#endif
316-
317310
template <bool Clockwise, typename P>
318311
void test_all()
319312
{
@@ -453,18 +446,14 @@ void test_all()
453446
test_one<multi_polygon_type, polygon_type>("rt_p19", rt_p19, join_miter, end_flat, 25.5637, 1.0);
454447
test_one<multi_polygon_type, polygon_type>("rt_p20", rt_p20, join_miter, end_flat, 25.4853, 1.0);
455448
test_one<multi_polygon_type, polygon_type>("rt_p21", rt_p21, join_miter, end_flat, 17.1716, 1.0);
456-
#if ! defined(BOOST_GEOMETRY_EXCLUDE)
457449
test_one<multi_polygon_type, polygon_type>("rt_p22", rt_p22, join_miter, end_flat, 26.5711, 1.0);
458-
#endif
459450

460451
test_one<multi_polygon_type, polygon_type>("rt_q1", rt_q1, join_miter, end_flat, 27, 1.0);
461452
test_one<multi_polygon_type, polygon_type>("rt_q2", rt_q2, join_miter, end_flat, 26.4853, 1.0);
462453
test_one<multi_polygon_type, polygon_type>("rt_q2", rt_q2, join_miter, end_flat, 0.9697, -0.25);
463454

464455
test_one<multi_polygon_type, polygon_type>("rt_r", rt_r, join_miter, end_flat, 21.0761, 1.0);
465-
#if ! defined(BOOST_GEOMETRY_EXCLUDE)
466456
test_one<multi_polygon_type, polygon_type>("rt_s1", rt_s1, join_miter, end_flat, 20.4853, 1.0);
467-
#endif
468457

469458
test_one<multi_polygon_type, polygon_type>("rt_s2", rt_s2, join_miter, end_flat, 24.6495, 1.0);
470459

@@ -543,7 +532,7 @@ int test_main(int, char* [])
543532
#endif
544533

545534
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
546-
BoostGeometryWriteExpectedFailures(1, 5);
535+
BoostGeometryWriteExpectedFailures(1, 1);
547536
#endif
548537

549538
return 0;

test/algorithms/set_operations/difference/difference.cpp

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -200,29 +200,23 @@ void test_all()
200200
8, 36, 2.43452380952381,
201201
7, 33, 3.18452380952381);
202202

203-
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
204-
// Fails with rescaling, a-b is partly generated, b-a does not have any output
205-
// It failed already in 1.59
206203
test_one<polygon, polygon, polygon>("case_58_iet",
207204
case_58[0], case_58[2],
208205
3, 12, 0.6666666667,
209206
1, -1, 11.1666666667,
210207
2, -1, 0.6666666667 + 11.1666666667);
211-
#endif
212208

213209
test_one<polygon, polygon, polygon>("case_80",
214210
case_80[0], case_80[1],
215211
1, 9, 44.5,
216212
1, 10, 84.5);
217213

218-
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
219214
// Fails without rescaling, holes are not subtracted
220215
test_one<polygon, polygon, polygon>("case_81",
221216
case_81[0], case_81[1],
222217
1, 8, 80.5,
223218
1, 8, 83.0,
224219
1, 12, 80.5 + 83.0);
225-
#endif
226220

227221
test_one<polygon, polygon, polygon>("case_100",
228222
case_100[0], case_100[1],
@@ -248,41 +242,31 @@ void test_all()
248242
TEST_DIFFERENCE(case_precision_2, 1, 14.0, 1, 8.0, 1);
249243
TEST_DIFFERENCE(case_precision_3, 1, 14.0, 1, 8.0, 1);
250244
TEST_DIFFERENCE(case_precision_4, 1, 14.0, 1, 8.0, 1);
251-
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
252245
TEST_DIFFERENCE(case_precision_5, 1, 14.0, 1, 8.0, count_set(1, 2));
253246
// Small optional sliver allowed, here and below
254247
TEST_DIFFERENCE(case_precision_6, optional(), 0.0, 1, 57.0, count_set(1, 2));
255-
#endif
256248
TEST_DIFFERENCE(case_precision_7, 1, 14.0, 1, 8.0, 1);
257249
TEST_DIFFERENCE(case_precision_8, 0, 0.0, 1, 59.0, 1);
258-
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
259250
TEST_DIFFERENCE(case_precision_9, optional(), 0.0, 1, 59.0, count_set(1, 2));
260251
TEST_DIFFERENCE(case_precision_10, optional(), 0.0, 1, 59.0, count_set(1, 2));
252+
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
261253
TEST_DIFFERENCE(case_precision_11, optional(), 0.0, 1, 59.0, 1);
262254
#endif
263255
TEST_DIFFERENCE(case_precision_12, 1, 12.0, 0, 0.0, 1);
264256
TEST_DIFFERENCE(case_precision_13, 1, BG_IF_KRAMER(12.00002, 12.0), 0, 0.0, 1);
265257
TEST_DIFFERENCE(case_precision_14, 1, 14.0, 1, 8.0, 1);
266258
TEST_DIFFERENCE(case_precision_15, 0, 0.0, 1, 59.0, 1);
267-
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
268259
TEST_DIFFERENCE(case_precision_16, optional(), 0.0, 1, 59.0, 1);
269-
#endif
270260
TEST_DIFFERENCE(case_precision_17, 0, 0.0, 1, 59.0, 1);
271261
TEST_DIFFERENCE(case_precision_18, 0, 0.0, 1, 59.0, 1);
272-
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
273262
TEST_DIFFERENCE(case_precision_19, 1, 0.0, 1, 59.0, 2);
274-
#endif
275263
TEST_DIFFERENCE(case_precision_20, 1, 14.0, 1, 8.0, 1);
276264
TEST_DIFFERENCE(case_precision_21, 1, 14.0, 1, 7.99999, 1);
277-
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
278265
TEST_DIFFERENCE(case_precision_22, optional(), 0.0, 1, 59.0, count_set(1, 2));
279266
TEST_DIFFERENCE(case_precision_23, optional(), 0.0, 1, 59.0, count_set(1, 2));
280-
#endif
281267
TEST_DIFFERENCE(case_precision_24, 1, 14.0, 1, 8.0, 1);
282268
TEST_DIFFERENCE(case_precision_25, 1, 14.0, 1, 7.99999, 1);
283-
#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) || defined(BOOST_GEOMETRY_TEST_FAILURES)
284269
TEST_DIFFERENCE(case_precision_26, optional(), 0.0, 1, 59.0, count_set(1, 2));
285-
#endif
286270

287271
test_one<polygon, polygon, polygon>("winded",
288272
winded[0], winded[1],
@@ -454,9 +438,9 @@ void test_all()
454438
ut_settings settings;
455439
settings.set_test_validity(BG_IF_RESCALED(false, true));
456440
TEST_DIFFERENCE_WITH(ggl_list_20190307_matthieu_1,
457-
BG_IF_KRAMER(2, 1), 0.18461532,
458-
BG_IF_KRAMER(2, 1), 0.617978,
459-
BG_IF_KRAMER(4, 2));
441+
count_set(1, 2), 0.18461532,
442+
count_set(1, 2), 0.617978,
443+
count_set(3, 4));
460444
TEST_DIFFERENCE_WITH(ggl_list_20190307_matthieu_2, 2, 12.357152, 0, 0.0, 2);
461445
}
462446

@@ -661,7 +645,7 @@ int test_main(int, char* [])
661645
#endif
662646

663647
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
664-
BoostGeometryWriteExpectedFailures(12, 15);
648+
BoostGeometryWriteExpectedFailures(12, 9);
665649
#endif
666650

667651
return 0;

test/algorithms/set_operations/union/union.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,13 +493,10 @@ void test_areal()
493493
1, 0, -1, 16.571);
494494
test_one<Polygon, Polygon, Polygon>("buffer_rt_g_rev", buffer_rt_g[1], buffer_rt_g[0],
495495
1, 0, -1, 16.571);
496-
#if ! defined(BOOST_GEOMETRY_EXCLUDE)
497496
test_one<Polygon, Polygon, Polygon>("buffer_rt_i", buffer_rt_i[0], buffer_rt_i[1],
498497
1, 0, -1, 13.6569);
499-
#endif
500498
test_one<Polygon, Polygon, Polygon>("buffer_rt_i_rev", buffer_rt_i[1], buffer_rt_i[0],
501499
1, 0, -1, 13.6569);
502-
503500
test_one<Polygon, Polygon, Polygon>("buffer_rt_j", buffer_rt_j[0], buffer_rt_j[1],
504501
1, 0, -1, 16.5711);
505502
test_one<Polygon, Polygon, Polygon>("buffer_rt_j_rev", buffer_rt_j[1], buffer_rt_j[0],
@@ -637,7 +634,7 @@ int test_main(int, char* [])
637634
#endif
638635

639636
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
640-
BoostGeometryWriteExpectedFailures(3, 10);
637+
BoostGeometryWriteExpectedFailures(3, 6);
641638
#endif
642639

643640
return 0;

0 commit comments

Comments
 (0)