Skip to content

Commit ee3509f

Browse files
Merge pull request #769 from barendgehrels/fix/start-turns
Fix/start turns
2 parents 4f128c4 + 271c5ad commit ee3509f

File tree

12 files changed

+204
-10
lines changed

12 files changed

+204
-10
lines changed

include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class piece_turn_visitor
266266

267267
typedef detail::overlay::get_turn_info
268268
<
269-
detail::overlay::assign_null_policy
269+
detail::overlay::assign_policy_only_start_turns
270270
> turn_policy;
271271

272272
turn_policy::apply(unique_sub_range1, unique_sub_range2,

include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct assign_disjoint_policy
7979
static bool const include_no_turn = true;
8080
static bool const include_degenerate = true;
8181
static bool const include_opposite = true;
82+
static bool const include_start_turn = false;
8283
};
8384

8485

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,8 @@ inline void enrich_intersection_points(Turns& turns,
491491
{
492492
detail::overlay::discard_closed_turns
493493
<
494-
OverlayType,
495-
target_operation
494+
OverlayType,
495+
target_operation
496496
>::apply(turns, clusters, geometry1, geometry2,
497497
strategy);
498498
detail::overlay::discard_open_turns

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

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,65 @@ struct equal : public base_turn_handler
830830
}
831831
};
832832

833+
template
834+
<
835+
typename TurnInfo
836+
>
837+
struct start : public base_turn_handler
838+
{
839+
template
840+
<
841+
typename UniqueSubRange1,
842+
typename UniqueSubRange2,
843+
typename IntersectionInfo,
844+
typename DirInfo,
845+
typename SideCalculator,
846+
typename UmbrellaStrategy
847+
>
848+
static inline bool apply(UniqueSubRange1 const& /*range_p*/,
849+
UniqueSubRange2 const& /*range_q*/,
850+
TurnInfo& ti,
851+
IntersectionInfo const& info,
852+
DirInfo const& dir_info,
853+
SideCalculator const& side,
854+
UmbrellaStrategy const& )
855+
{
856+
#if defined(BOOST_GEOMETRY_USE_RESCALING)
857+
// With rescaling, start turns are not needed.
858+
return false;
859+
#endif
860+
861+
// Start turns have either how_a = -1, or how_b = -1 (either p leaves or q leaves)
862+
BOOST_GEOMETRY_ASSERT(dir_info.how_a != dir_info.how_b);
863+
BOOST_GEOMETRY_ASSERT(dir_info.how_a == -1 || dir_info.how_b == -1);
864+
BOOST_GEOMETRY_ASSERT(dir_info.how_a == 0 || dir_info.how_b == 0);
865+
866+
if (dir_info.how_b == -1)
867+
{
868+
// p --------------->
869+
// |
870+
// | q q leaves
871+
// v
872+
//
873+
874+
int const side_qj_p1 = side.qj_wrt_p1();
875+
ui_else_iu(side_qj_p1 == -1, ti);
876+
}
877+
else if (dir_info.how_a == -1)
878+
{
879+
// p leaves
880+
int const side_pj_q1 = side.pj_wrt_q1();
881+
ui_else_iu(side_pj_q1 == 1, ti);
882+
}
883+
884+
// Copy intersection point
885+
assign_point(ti, method_start, info, 0);
886+
return true;
887+
}
888+
889+
};
890+
891+
833892
template
834893
<
835894
typename TurnInfo,
@@ -1194,6 +1253,15 @@ struct assign_null_policy
11941253
static bool const include_no_turn = false;
11951254
static bool const include_degenerate = false;
11961255
static bool const include_opposite = false;
1256+
static bool const include_start_turn = false;
1257+
};
1258+
1259+
struct assign_policy_only_start_turns
1260+
{
1261+
static bool const include_no_turn = false;
1262+
static bool const include_degenerate = false;
1263+
static bool const include_opposite = false;
1264+
static bool const include_start_turn = true;
11971265
};
11981266

11991267
/*!
@@ -1257,9 +1325,24 @@ struct get_turn_info
12571325
bool handle_as_equal = method == 'e';
12581326
bool const handle_as_collinear = method == 'c';
12591327
bool const handle_as_degenerate = method == '0';
1328+
bool const handle_as_start = method == 's';
12601329

1261-
// (angle, from, start)
1262-
bool const do_only_convert = method == 'a' || method == 'f' || method == 's';
1330+
// (angle, from)
1331+
bool do_only_convert = method == 'a' || method == 'f';
1332+
1333+
if (handle_as_start)
1334+
{
1335+
// It is in some cases necessary to handle a start turn
1336+
if (AssignPolicy::include_start_turn
1337+
&& start<TurnInfo>::apply(range_p, range_q, tp, inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy))
1338+
{
1339+
*out++ = tp;
1340+
}
1341+
else
1342+
{
1343+
do_only_convert = true;
1344+
}
1345+
}
12631346

12641347
if (handle_as_touch_interior)
12651348
{

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,46 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
507507
}
508508
}
509509

510+
template<typename Turns, typename Clusters>
511+
inline void discard_start_turns(Turns& turns, Clusters& clusters)
512+
{
513+
for (auto& nv : clusters)
514+
{
515+
cluster_info& cinfo = nv.second;
516+
auto& indices = cinfo.turn_indices;
517+
std::size_t start_count{0};
518+
for (signed_size_type index : indices)
519+
{
520+
auto const& turn = turns[index];
521+
if (turn.method == method_start)
522+
{
523+
start_count++;
524+
}
525+
}
526+
if (start_count == 0 && start_count == indices.size())
527+
{
528+
// There are no start turns, or all turns in the cluster are start turns.
529+
continue;
530+
}
531+
532+
// Discard the start turns and simultaneously erase them from the indices
533+
for (auto it = indices.begin(); it != indices.end();)
534+
{
535+
auto& turn = turns[*it];
536+
if (turn.method == method_start)
537+
{
538+
turn.discarded = true;
539+
turn.cluster_id = -1;
540+
it = indices.erase(it);
541+
}
542+
else
543+
{
544+
++it;
545+
}
546+
}
547+
}
548+
}
549+
510550
template <typename Geometry0, typename Geometry1>
511551
inline segment_identifier get_preceding_segment_id(segment_identifier const& id,
512552
Geometry0 const& geometry0, Geometry1 const& geometry1)
@@ -700,6 +740,8 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters,
700740
// on turns which are discarded afterwards
701741
set_colocation<OverlayType>(turns, clusters);
702742

743+
discard_start_turns(turns, clusters);
744+
703745
if (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
704746
{
705747
discard_interior_exterior_turns
@@ -783,7 +825,7 @@ inline bool fill_sbs(Sbs& sbs, Point& turn_point,
783825
{
784826
signed_size_type turn_index = *sit;
785827
turn_type const& turn = turns[turn_index];
786-
if (first )
828+
if (first)
787829
{
788830
turn_point = turn.point;
789831
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class linear_linear_linestring
138138
static bool const include_no_turn = false;
139139
static bool const include_degenerate = EnableDegenerateTurns;
140140
static bool const include_opposite = false;
141+
static bool const include_start_turn = false;
141142
};
142143

143144

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ std::cout << "get turns" << std::endl;
306306
geometry::get_turns
307307
<
308308
Reverse1, Reverse2,
309-
detail::overlay::assign_null_policy
309+
assign_policy_only_start_turns
310310
>(geometry1, geometry2, strategy, robust_policy, turns, policy);
311311

312312
visitor.visit_turns(1, turns);
@@ -318,12 +318,12 @@ std::cout << "get turns" << std::endl;
318318
// and if necessary (e.g.: multi-geometry, polygon with interior rings)
319319
if (needs_self_turns<Geometry1>::apply(geometry1))
320320
{
321-
self_get_turn_points::self_turns<Reverse1, assign_null_policy>(geometry1,
321+
self_get_turn_points::self_turns<Reverse1, assign_policy_only_start_turns>(geometry1,
322322
strategy, robust_policy, turns, policy, 0);
323323
}
324324
if (needs_self_turns<Geometry2>::apply(geometry2))
325325
{
326-
self_get_turn_points::self_turns<Reverse2, assign_null_policy>(geometry2,
326+
self_get_turn_points::self_turns<Reverse2, assign_policy_only_start_turns>(geometry2,
327327
strategy, robust_policy, turns, policy, 1);
328328
}
329329
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum method_type
3434
method_touch_interior,
3535
method_collinear,
3636
method_equal,
37+
method_start,
3738
method_error
3839
};
3940

include/boost/geometry/algorithms/detail/touches/implementation.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ struct areal_interrupt_policy
181181
return true;
182182
}
183183
break;
184+
case overlay::method_start :
184185
case overlay::method_none :
185186
case overlay::method_disjoint :
186187
case overlay::method_error :

test/algorithms/buffer/buffer_multi_polygon.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,52 @@ static std::string const rt_v3
292292
static std::string const rt_v4
293293
= "MULTIPOLYGON(((5 4,5 5,6 5,6 4,5 4)),((7 1,6 1,7 2,7 1)),((7 1,8 1,8 0,7 0,7 1)),((6 1,5 1,5 2,6 1)))";
294294

295+
// From 2020 runs of robustness test recursive_polygons_buffer, without rescaling
296+
// For the same problem there can be multiple cases, but details differ
297+
298+
// Cases missing a turn, or needing a start turn
299+
static std::string const nores_mt_1
300+
= "MULTIPOLYGON(((4 8,4 9,5 9,4 8)),((3 6,3 7,4 6,3 6)))";
301+
302+
static std::string const nores_mt_2
303+
= "MULTIPOLYGON(((5 3,6 4,6 3,5 3)),((4 4,3 4,4 5,5 5,4 4)),((4 5,3 5,3 6,4 5)))";
304+
305+
static std::string const nores_mt_3
306+
= "MULTIPOLYGON(((7 4,7 5,8 5,8 4,7 4)),((2 6,2 7,3 6,2 6)),((3 10,4 10,4 9,4 8,3 8,3 10)))";
307+
308+
static std::string const nores_mt_4
309+
= "MULTIPOLYGON(((6 8,6 9,7 9,6 8)),((1 5,1 6,2 6,1 5)),((7 7,8 8,8 7,7 7)),((0 3,0 4,1 3,0 3)))";
310+
311+
static std::string const nores_mt_5
312+
= "MULTIPOLYGON(((4 3,4 4,5 4,5 3,4 3)),((3 1,3 2,4 1,3 1)),((1 6,2 7,2 6,1 6)),((3 6,4 5,3 4,3 6)))";
313+
314+
static std::string const nores_mt_6
315+
= "MULTIPOLYGON(((5 7,5 6,4 6,4 5,4 4,3 4,3 6,3 7,5 7)))";
316+
317+
// Cases generating an extra turn, and/or a cluster not yet handled correctly
318+
static std::string const nores_et_1
319+
= "MULTIPOLYGON(((5 7,5 8,6 8,5 7)),((5 4,5 5,6 4,5 4)),((3 6,4 7,4 6,3 6)))";
320+
321+
static std::string const nores_et_2
322+
= "MULTIPOLYGON(((4 2,5 3,5 2,4 2)),((6 3,6 4,7 4,7 3,6 3)),((7 2,8 3,8 2,7 2)),((4 4,4 5,5 5,5 4,4 4)))";
323+
324+
static std::string const nores_et_3
325+
= "MULTIPOLYGON(((3 1,3 2,4 2,4 1,3 1)),((5 4,5 3,4 3,5 4)),((5 3,6 2,5 2,5 3)),((8 1,7 1,6 1,7 2,7 3,7.5 2.5,8 3,8 1)))";
326+
327+
static std::string const nores_et_4
328+
= "MULTIPOLYGON(((4 7,4 8,5 8,5 7,4 7)),((3 5,3 6,4 5,3 5)),((1 6,2 7,2 6,1 6)))";
329+
330+
static std::string const nores_et_5
331+
= "MULTIPOLYGON(((3 2,3 3,4 3,4 2,3 2)),((0 3,0 4,1 3,0 3)),((2 2,2 1,1 1,2 2)))";
332+
333+
334+
// Cases having wrong next turn information, somehow, taking the "i" (intersection),
335+
// which is wrong for buffer (it should take the "u" like union)
336+
static std::string const nores_wn_1
337+
= "MULTIPOLYGON(((8 3,8 4,9 4,9 3,8 3)),((9 5,9 6,10 5,9 5)),((8 8,9 9,9 8,8 8)),((8 8,8 7,7 7,8 8)))";
338+
static std::string const nores_wn_2
339+
= "MULTIPOLYGON(((9 5,9 6,10 5,9 5)),((8 8,8 7,7 7,7 8,8 8)),((8 8,9 9,9 8,8 8)))";
340+
295341

296342
static std::string const neighbouring
297343
= "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((10 10,10 20,20 20,20 10,10 10)))";
@@ -498,6 +544,23 @@ void test_all()
498544
test_one<multi_polygon_type, polygon_type>("rt_v3", rt_v3, join_round32, end_flat, 22.9158, 1.0);
499545
test_one<multi_polygon_type, polygon_type>("rt_v4", rt_v4, join_round32, end_flat, 23.4146, 1.0);
500546

547+
test_one<multi_polygon_type, polygon_type>("nores_mt_1", nores_mt_1, join_round32, end_flat, 13.4113, 1.0);
548+
test_one<multi_polygon_type, polygon_type>("nores_mt_2", nores_mt_2, join_round32, end_flat, 17.5265, 1.0);
549+
test_one<multi_polygon_type, polygon_type>("nores_mt_3", nores_mt_3, join_round32, end_flat, 25.6091, 1.0);
550+
test_one<multi_polygon_type, polygon_type>("nores_mt_4", nores_mt_4, join_round32, end_flat, 26.0946, 1.0);
551+
test_one<multi_polygon_type, polygon_type>("nores_mt_5", nores_mt_5, join_round32, end_flat, 26.4375, 1.0);
552+
test_one<multi_polygon_type, polygon_type>("nores_mt_6", nores_mt_6, join_round32, end_flat, 16.9018, 1.0);
553+
554+
#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
555+
test_one<multi_polygon_type, polygon_type>("nores_et_1", nores_et_1, join_round32, end_flat, 18.9866, 1.0);
556+
test_one<multi_polygon_type, polygon_type>("nores_et_2", nores_et_2, join_round32, end_flat, 23.8389, 1.0);
557+
test_one<multi_polygon_type, polygon_type>("nores_et_3", nores_et_3, join_round32, end_flat, 26.9030, 1.0);
558+
test_one<multi_polygon_type, polygon_type>("nores_et_4", nores_et_4, join_round32, end_flat, 19.9626, 1.0);
559+
test_one<multi_polygon_type, polygon_type>("nores_et_5", nores_et_5, join_round32, end_flat, 19.9615, 1.0);
560+
test_one<multi_polygon_type, polygon_type>("nores_wn_1", nores_wn_1, join_round32, end_flat, 23.7659, 1.0);
561+
test_one<multi_polygon_type, polygon_type>("nores_wn_2", nores_wn_2, join_round32, end_flat, 18.2669, 1.0);
562+
#endif
563+
501564
test_one<multi_polygon_type, polygon_type>("neighbouring_small",
502565
neighbouring,
503566
join_round32, end_round32, 128.0, -1.0);
@@ -541,7 +604,7 @@ int test_main(int, char* [])
541604
#endif
542605

543606
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
544-
BoostGeometryWriteExpectedFailures(1, 1, 2, 3);
607+
BoostGeometryWriteExpectedFailures(1, 14, 2, 11);
545608
#endif
546609

547610
return 0;

0 commit comments

Comments
 (0)