Skip to content

Commit 4597a16

Browse files
committed
[traverse] reverse meaning of isolation in difference
1 parent afa1f45 commit 4597a16

File tree

6 files changed

+112
-62
lines changed

6 files changed

+112
-62
lines changed

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ struct is_self_turn_check
2626
template <typename Turn>
2727
static inline bool apply(Turn const& turn)
2828
{
29-
return turn.operations[0].seg_id.source_index
30-
== turn.operations[1].seg_id.source_index;
29+
return turn.is_self();
3130
}
3231
};
3332

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

+55-43
Original file line numberDiff line numberDiff line change
@@ -621,31 +621,32 @@ public :
621621
return m_turns[rp.turn_index].operations[rp.operation_index];
622622
}
623623

624-
inline sort_by_side::rank_type select_rank(sbs_type const& sbs,
625-
bool skip_isolated) const
624+
inline sort_by_side::rank_type select_rank(sbs_type const& sbs) const
626625
{
626+
static bool const is_intersection
627+
= target_operation == operation_intersection;
628+
627629
// Take the first outgoing rank corresponding to incoming region,
628630
// or take another region if it is not isolated
629-
turn_operation_type const& incoming_op
630-
= operation_from_rank(sbs.m_ranked_points.front());
631+
auto const& in_op = operation_from_rank(sbs.m_ranked_points.front());
631632

632633
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
633634
{
634-
typename sbs_type::rp const& rp = sbs.m_ranked_points[i];
635+
auto const& rp = sbs.m_ranked_points[i];
635636
if (rp.rank == 0 || rp.direction == sort_by_side::dir_from)
636637
{
637638
continue;
638639
}
639-
turn_operation_type const& op = operation_from_rank(rp);
640+
auto const& out_op = operation_from_rank(rp);
640641

641-
if (op.operation != target_operation
642-
&& op.operation != operation_continue)
642+
if (out_op.operation != target_operation
643+
&& out_op.operation != operation_continue)
643644
{
644645
continue;
645646
}
646647

647-
if (op.enriched.region_id == incoming_op.enriched.region_id
648-
|| (skip_isolated && ! op.enriched.isolated))
648+
if (in_op.enriched.region_id == out_op.enriched.region_id
649+
|| (is_intersection && ! out_op.enriched.isolated))
649650
{
650651
// Region corresponds to incoming region, or (for intersection)
651652
// there is a non-isolated other region which should be taken
@@ -660,7 +661,7 @@ public :
660661
int& op_index, sbs_type const& sbs,
661662
signed_size_type start_turn_index, int start_op_index) const
662663
{
663-
sort_by_side::rank_type const selected_rank = select_rank(sbs, false);
664+
sort_by_side::rank_type const selected_rank = select_rank(sbs);
664665

665666
int current_priority = 0;
666667
for (std::size_t i = 1; i < sbs.m_ranked_points.size(); i++)
@@ -688,49 +689,59 @@ public :
688689
inline bool analyze_cluster_intersection(signed_size_type& turn_index,
689690
int& op_index, sbs_type const& sbs) const
690691
{
691-
sort_by_side::rank_type const selected_rank = select_rank(sbs, true);
692+
// Select the rank based on regions and isolation
693+
sort_by_side::rank_type const selected_rank = select_rank(sbs);
692694

693-
if (selected_rank > 0)
695+
if (selected_rank <= 0)
694696
{
695-
typename turn_operation_type::comparable_distance_type
696-
min_remaining_distance = 0;
697+
return false;
698+
}
697699

698-
std::size_t selected_index = sbs.m_ranked_points.size();
699-
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
700+
// From these ranks, select the index: the first, or the one with
701+
// the smallest remaining distance
702+
typename turn_operation_type::comparable_distance_type
703+
min_remaining_distance = 0;
704+
705+
std::size_t selected_index = sbs.m_ranked_points.size();
706+
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
707+
{
708+
auto const& ranked_point = sbs.m_ranked_points[i];
709+
710+
if (ranked_point.rank > selected_rank)
711+
{
712+
break;
713+
}
714+
else if (ranked_point.rank == selected_rank)
700715
{
701-
typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[i];
716+
auto const& op = operation_from_rank(ranked_point);
702717

703-
if (ranked_point.rank == selected_rank)
718+
if (op.visited.finalized())
704719
{
705-
turn_operation_type const& op = operation_from_rank(ranked_point);
706-
707-
if (op.visited.finalized())
708-
{
709-
// This direction is already traveled before, the same
710-
// cannot be traveled again
711-
continue;
712-
}
713-
714-
// Take turn with the smallest remaining distance
715-
if (selected_index == sbs.m_ranked_points.size()
716-
|| op.remaining_distance < min_remaining_distance)
717-
{
718-
selected_index = i;
719-
min_remaining_distance = op.remaining_distance;
720-
}
720+
// This direction is already traveled,
721+
// it cannot be traveled again
722+
continue;
721723
}
722-
}
723724

724-
if (selected_index < sbs.m_ranked_points.size())
725-
{
726-
typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[selected_index];
727-
turn_index = ranked_point.turn_index;
728-
op_index = ranked_point.operation_index;
729-
return true;
725+
if (selected_index == sbs.m_ranked_points.size()
726+
|| op.remaining_distance < min_remaining_distance)
727+
{
728+
// It was unassigned or it is better
729+
selected_index = i;
730+
min_remaining_distance = op.remaining_distance;
731+
}
730732
}
731733
}
732734

733-
return false;
735+
if (selected_index == sbs.m_ranked_points.size())
736+
{
737+
// Should not happen, there must be points with the selected rank
738+
return false;
739+
}
740+
741+
auto const& ranked_point = sbs.m_ranked_points[selected_index];
742+
turn_index = ranked_point.turn_index;
743+
op_index = ranked_point.operation_index;
744+
return true;
734745
}
735746

736747
inline bool fill_sbs(sbs_type& sbs,
@@ -819,6 +830,7 @@ public :
819830
return result;
820831
}
821832

833+
// Analyzes a non-clustered "ii" intersection, as if it is clustered.
822834
inline bool analyze_ii_intersection(signed_size_type& turn_index, int& op_index,
823835
turn_type const& current_turn,
824836
segment_identifier const& previous_seg_id)

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

+39-10
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ struct traversal_switch_detector
8989

9090
enum isolation_type
9191
{
92-
isolation_unknown = -1,
9392
isolation_no = 0,
9493
isolation_yes = 1,
9594
isolation_multiple = 2
@@ -121,7 +120,7 @@ struct traversal_switch_detector
121120
struct region_properties
122121
{
123122
signed_size_type region_id = -1;
124-
isolation_type isolated = isolation_unknown;
123+
isolation_type isolated = isolation_no;
125124
set_type unique_turn_ids;
126125
connection_map connected_region_counts;
127126
};
@@ -374,7 +373,7 @@ struct traversal_switch_detector
374373
{
375374
region_properties& properties = key_val.second;
376375

377-
if (properties.isolated == isolation_unknown
376+
if (properties.isolated == isolation_no
378377
&& has_only_isolated_children(properties))
379378
{
380379
properties.isolated = isolation_yes;
@@ -388,13 +387,36 @@ struct traversal_switch_detector
388387
{
389388
for (turn_type& turn : m_turns)
390389
{
390+
// For difference, for the input walked through in reverse,
391+
// the meaning is reversed: what is isolated is actually not,
392+
// and vice versa.
393+
bool const reverseMeaningInTurn
394+
= (Reverse1 || Reverse2)
395+
&& ! turn.is_self()
396+
&& ! turn.is_clustered()
397+
&& uu_or_ii(turn)
398+
&& turn.operations[0].enriched.region_id
399+
!= turn.operations[1].enriched.region_id;
400+
391401
for (auto& op : turn.operations)
392402
{
393403
auto mit = m_connected_regions.find(op.enriched.region_id);
394404
if (mit != m_connected_regions.end())
395405
{
406+
bool const reverseMeaningInOp
407+
= reverseMeaningInTurn
408+
&& ((op.seg_id.source_index == 0 && Reverse1)
409+
|| (op.seg_id.source_index == 1 && Reverse2));
410+
411+
// It is assigned to isolated if it's property is "Yes",
412+
// (one connected interior, or chained).
413+
// "Multiple" doesn't count for isolation,
414+
// neither for intersection, neither for difference.
396415
region_properties const& prop = mit->second;
397-
op.enriched.isolated = prop.isolated == isolation_yes;
416+
op.enriched.isolated
417+
= reverseMeaningInOp
418+
? prop.isolated == isolation_no
419+
: prop.isolated == isolation_yes;
398420
}
399421
}
400422
}
@@ -478,8 +500,12 @@ struct traversal_switch_detector
478500
// Discarded turns don't connect rings to the same region
479501
// Also xx are not relevant
480502
// (otherwise discarded colocated uu turn could make a connection)
481-
return ! turn.discarded
482-
&& ! turn.both(operation_blocked);
503+
return ! turn.discarded && ! turn.both(operation_blocked);
504+
}
505+
506+
inline bool uu_or_ii(turn_type const& turn) const
507+
{
508+
return turn.both(operation_union) || turn.both(operation_intersection);
483509
}
484510

485511
inline bool connects_same_region(turn_type const& turn) const
@@ -492,7 +518,7 @@ struct traversal_switch_detector
492518
if (! turn.is_clustered())
493519
{
494520
// If it is a uu/ii-turn (non clustered), it is never same region
495-
return ! (turn.both(operation_union) || turn.both(operation_intersection));
521+
return ! uu_or_ii(turn);
496522
}
497523

498524
if (BOOST_GEOMETRY_CONDITION(target_operation == operation_union))
@@ -568,7 +594,10 @@ struct traversal_switch_detector
568594
void iterate()
569595
{
570596
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
571-
std::cout << "BEGIN SWITCH DETECTOR (region_ids and isolation)" << std::endl;
597+
std::cout << "BEGIN SWITCH DETECTOR (region_ids and isolation)"
598+
<< (Reverse1 ? " REVERSE_1" : "")
599+
<< (Reverse2 ? " REVERSE_2" : "")
600+
<< std::endl;
572601
#endif
573602

574603
// Collect turns per ring
@@ -613,14 +642,14 @@ struct traversal_switch_detector
613642
{
614643
turn_type const& turn = m_turns[turn_index];
615644

616-
if ((turn.both(operation_union) || turn.both(operation_intersection))
617-
&& ! turn.is_clustered())
645+
if (uu_or_ii(turn) && ! turn.is_clustered())
618646
{
619647
std::cout << (turn.both(operation_union) ? "UU" : "II")
620648
<< " " << turn_index
621649
<< " (" << geometry::get<0>(turn.point)
622650
<< ", " << geometry::get<1>(turn.point) << ")"
623651
<< " -> " << std::boolalpha
652+
<< " [" << turn.operations[0].seg_id.source_index << "/" << turn.operations[1].seg_id.source_index << "] "
624653
<< "(" << turn.operations[0].enriched.region_id
625654
<< " " << turn.operations[0].enriched.isolated
626655
<< ") / (" << turn.operations[1].enriched.region_id

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

+5
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ struct turn_info
138138
{
139139
return cluster_id > 0;
140140
}
141+
inline bool is_self() const
142+
{
143+
return operations[0].seg_id.source_index
144+
== operations[1].seg_id.source_index;
145+
}
141146

142147
private :
143148
inline bool has12(operation_type type1, operation_type type2) const

test/algorithms/set_operations/difference/difference_multi.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,8 @@ void test_areal()
209209
#endif
210210
}
211211

212-
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
213-
// Generates a polygon with two interiors, i/o a multipoly with 3 rings
214-
TEST_DIFFERENCE(issue_869_a, 3, 3600, 0, 0, 1);
215-
#endif
212+
// Requires reveral of isolation in ii turns. There should be 3 rings.
213+
TEST_DIFFERENCE(issue_869_a, 3, 3600, 0, 0, 3);
216214

217215
TEST_DIFFERENCE(issue_888_34, 22, 0.2506824, 6, 0.0253798, 28);
218216
TEST_DIFFERENCE(issue_888_37, 15, 0.0451408, 65, 0.3014843, 80);

test/algorithms/set_operations/difference/test_difference.hpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -322,18 +322,25 @@ std::string test_one(std::string const& caseid,
322322
bg::correct(g1);
323323
bg::correct(g2);
324324

325-
std::string result = test_difference<OutputType>(caseid + "_a", g1, g2,
325+
std::string result;
326+
327+
#if ! defined(BOOST_GEOMETRY_TEST_DIFFERENCE_ONLY_B)
328+
result = test_difference<OutputType>(caseid + "_a", g1, g2,
326329
expected_count1, expected_rings_count1, expected_point_count1,
327330
expected_area1, false, settings);
328-
329-
#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
331+
#endif
332+
#if defined(BOOST_GEOMETRY_TEST_DIFFERENCE_ONLY_A)
330333
return result;
331334
#endif
332335

333336
test_difference<OutputType>(caseid + "_b", g2, g1,
334337
expected_count2, expected_rings_count2, expected_point_count2,
335338
expected_area2, false, settings);
336339

340+
#if defined(BOOST_GEOMETRY_TEST_DIFFERENCE_ONLY_B)
341+
return result;
342+
#endif
343+
337344
#if ! defined(BOOST_GEOMETRY_TEST_ALWAYS_CHECK_SYMDIFFERENCE)
338345
if (settings.sym_difference)
339346
#endif

0 commit comments

Comments
 (0)