Skip to content

Commit 02b1632

Browse files
barendgehrelstinko92
authored andcommitted
[overlay] Fix possible instabilities in sort_by_side
plus some minor code refactoring
1 parent 3f46f47 commit 02b1632

File tree

2 files changed

+82
-43
lines changed

2 files changed

+82
-43
lines changed

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

+44-22
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,46 @@ struct is_turn_index
752752
signed_size_type m_index;
753753
};
754754

755+
template
756+
<
757+
typename Sbs,
758+
typename Point,
759+
typename Turns,
760+
typename Geometry1,
761+
typename Geometry2
762+
>
763+
inline bool fill_sbs(Sbs& sbs, Point& turn_point,
764+
cluster_info const& cinfo,
765+
Turns const& turns,
766+
Geometry1 const& geometry1, Geometry2 const& geometry2)
767+
{
768+
typedef typename boost::range_value<Turns>::type turn_type;
769+
770+
std::set<signed_size_type> const& ids = cinfo.turn_indices;
771+
772+
if (ids.empty())
773+
{
774+
return false;
775+
}
776+
777+
bool first = true;
778+
for (std::set<signed_size_type>::const_iterator sit = ids.begin();
779+
sit != ids.end(); ++sit)
780+
{
781+
signed_size_type turn_index = *sit;
782+
turn_type const& turn = turns[turn_index];
783+
if (first )
784+
{
785+
turn_point = turn.point;
786+
}
787+
for (int i = 0; i < 2; i++)
788+
{
789+
sbs.add(turn.operations[i], turn_index, i, geometry1, geometry2, first);
790+
first = false;
791+
}
792+
}
793+
return true;
794+
}
755795

756796
template
757797
<
@@ -783,32 +823,14 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
783823
mit != clusters.end(); ++mit)
784824
{
785825
cluster_info& cinfo = mit->second;
786-
std::set<signed_size_type> const& ids = cinfo.turn_indices;
787-
if (ids.empty())
788-
{
789-
continue;
790-
}
791826

792827
sbs_type sbs(strategy);
793828
point_type turn_point; // should be all the same for all turns in cluster
794-
795-
bool first = true;
796-
for (std::set<signed_size_type>::const_iterator sit = ids.begin();
797-
sit != ids.end(); ++sit)
829+
if (! fill_sbs(sbs, turn_point, cinfo, turns, geometry1, geometry2))
798830
{
799-
signed_size_type turn_index = *sit;
800-
turn_type const& turn = turns[turn_index];
801-
if (first)
802-
{
803-
turn_point = turn.point;
804-
}
805-
for (int i = 0; i < 2; i++)
806-
{
807-
turn_operation_type const& op = turn.operations[i];
808-
sbs.add(op, turn_index, i, geometry1, geometry2, first);
809-
first = false;
810-
}
831+
continue;
811832
}
833+
812834
sbs.apply(turn_point);
813835

814836
sbs.find_open();
@@ -823,7 +845,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
823845
// polygons
824846
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
825847
{
826-
const typename sbs_type::rp& ranked = sbs.m_ranked_points[i];
848+
typename sbs_type::rp const& ranked = sbs.m_ranked_points[i];
827849
turn_type& turn = turns[ranked.turn_index];
828850
turn_operation_type& op = turn.operations[ranked.operation_index];
829851

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

+38-21
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,14 @@ struct less_by_side
186186
return on_same(first, second);
187187
}
188188

189-
int const side_first_wrt_second = -side_second_wrt_first;
189+
int const side_first_wrt_second = m_strategy.apply(m_turn_point, second.point, first.point);
190+
if (side_second_wrt_first != -side_first_wrt_second)
191+
{
192+
// (FP) accuracy error in side calculation, the sides are not opposite.
193+
// In that case they can be handled as collinear.
194+
// If not, then the sort-order might not be stable.
195+
return on_same(first, second);
196+
}
190197

191198
// Both are on same side, and not collinear
192199
// Union: return true if second is right w.r.t. first, so -1,
@@ -367,9 +374,11 @@ public :
367374
}
368375
}
369376

370-
template <signed_size_type segment_identifier::*Member, typename Map>
371-
void find_open_generic(Map& handled, bool check)
377+
void find_open_by_piece_index()
372378
{
379+
// For buffers, use piece index
380+
std::set<signed_size_type> handled;
381+
373382
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
374383
{
375384
const rp& ranked = m_ranked_points[i];
@@ -378,39 +387,47 @@ public :
378387
continue;
379388
}
380389

381-
signed_size_type const& index = ranked.seg_id.*Member;
382-
if (check && (index < 0 || index > 1))
390+
signed_size_type const& index = ranked.seg_id.piece_index;
391+
if (handled.count(index) > 0)
383392
{
384-
// Should not occur
385393
continue;
386394
}
387-
if (! handled[index])
395+
find_polygons_for_source<&segment_identifier::piece_index>(index, i);
396+
handled.insert(index);
397+
}
398+
}
399+
400+
void find_open_by_source_index()
401+
{
402+
// Check for source index 0 and 1
403+
bool handled[2] = {false, false};
404+
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
405+
{
406+
const rp& ranked = m_ranked_points[i];
407+
if (ranked.direction != dir_from)
408+
{
409+
continue;
410+
}
411+
412+
signed_size_type const& index = ranked.seg_id.source_index;
413+
if (index < 0 || index > 1 || handled[index])
388414
{
389-
find_polygons_for_source<Member>(index, i);
390-
handled[index] = true;
415+
continue;
391416
}
417+
find_polygons_for_source<&segment_identifier::source_index>(index, i);
418+
handled[index] = true;
392419
}
393420
}
394421

395422
void find_open()
396423
{
397424
if (BOOST_GEOMETRY_CONDITION(OverlayType == overlay_buffer))
398425
{
399-
// For buffers, use piece index
400-
std::map<signed_size_type, bool> handled;
401-
find_open_generic
402-
<
403-
&segment_identifier::piece_index
404-
>(handled, false);
426+
find_open_by_piece_index();
405427
}
406428
else
407429
{
408-
// For other operations, by source (there should only source 0,1)
409-
bool handled[2] = {false, false};
410-
find_open_generic
411-
<
412-
&segment_identifier::source_index
413-
>(handled, true);
430+
find_open_by_source_index();
414431
}
415432
}
416433

0 commit comments

Comments
 (0)