From f6f2b28b0ee4b13be8858050d28825cd4e8cfe80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 15 Feb 2022 15:47:48 +0100 Subject: [PATCH 1/4] make the crossing point computation deterministic --- .../Intersections_2/Segment_2_Segment_2.h | 74 ++++++++++++++----- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h b/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h index d912b2faf30..19cfb0c0548 100644 --- a/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h +++ b/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h @@ -34,6 +34,22 @@ namespace Intersections { namespace internal { +// indices of lexicographically smallest endpoints +// depending on config parameter of S2S2_inter_info. +// {s1_id0,s1_id1,s2_id0,s2_id1} +constexpr int s2s2_id[8][4] = +{ +// seg1 seg2 + { 0,1, 0,1 }, + { 0,1, 1,0 }, + { 1,0, 0,1 }, + { 1,0, 1,0 }, + { 0,1, 0,1 }, + { 1,0, 0,1 }, + { 0,1, 1,0 }, + { 1,0, 1,0 } +}; + // struct used to report the combinaric of the intersection // of 2 2D segments. // More information could be gathered if exposed in do_intersect. @@ -43,9 +59,20 @@ struct S2S2_inter_info bool inter = false; bool dim = 0; std::array pt_ids = {-1,-1}; + // integer in [0,7] indicating segment endpoint ordering for determinism + // 0: p0 < p1 - p2 < p3 - p0 < p2 + // 1: p0 < p1 - p3 < p2 - p0 < p3 + // 2: p1 < p0 - p2 < p3 - p1 < p2 + // 3: p1 < p0 - p3 < p2 - p1 < p3 + // 4: p2 < p3 - p0 < p1 - p2 < p0 + // 5: p2 < p3 - p1 < p0 - p2 < p1 + // 6: p3 < p2 - p0 < p1 - p3 < p0 + // 7: p3 < p2 - p1 < p0 - p3 < p1 + int config; - S2S2_inter_info(bool inter) + S2S2_inter_info(bool inter, int c=-1) : inter(inter) + , config(c) {} // intersection is an input endpoint @@ -77,7 +104,7 @@ seg_seg_do_intersect_crossing( const typename K::Point_2& p1, const typename K::Point_2& p2, const typename K::Point_2& p3, const typename K::Point_2& p4, int /* i1 */, int i2, int i3, int /* i4 */, - const K& k, bool extra_test) + const K& k, bool extra_test, int config) { switch (make_certain(k.orientation_2_object()(p1,p2,p3))) { case LEFT_TURN: @@ -89,7 +116,7 @@ seg_seg_do_intersect_crossing( case RIGHT_TURN: return S2S2_inter_info(false); case LEFT_TURN: - return S2S2_inter_info(true); + return S2S2_inter_info(true, config); default: CGAL_unreachable(); } @@ -101,7 +128,7 @@ seg_seg_do_intersect_crossing( case COLLINEAR: return S2S2_inter_info(i2); case RIGHT_TURN: - return S2S2_inter_info(true); + return S2S2_inter_info(true, config); case LEFT_TURN: return S2S2_inter_info(false); default: @@ -138,7 +165,7 @@ seg_seg_do_intersect_contained( const typename K::Point_2& p1, const typename K::Point_2& p2, const typename K::Point_2& p3, const typename K::Point_2& p4, int /* i1 */, int /* i2 */, int i3, int i4, - const K& k, bool extra_test) + const K& k, bool extra_test, int config) { switch (make_certain(k.orientation_2_object()(p1,p2,p3))) { case LEFT_TURN: @@ -148,7 +175,7 @@ seg_seg_do_intersect_contained( case COLLINEAR: return S2S2_inter_info(i4); case RIGHT_TURN: - return S2S2_inter_info(true); + return S2S2_inter_info(true, config); case LEFT_TURN: return S2S2_inter_info(false); default: @@ -164,7 +191,7 @@ seg_seg_do_intersect_contained( case RIGHT_TURN: return S2S2_inter_info(false); case LEFT_TURN: - return S2S2_inter_info(true); + return S2S2_inter_info(true, config); default: CGAL_unreachable(); } @@ -215,8 +242,8 @@ do_intersect_with_info(const typename K::Segment_2 &seg1, typename K::Compare_xy_2 compare_xy; // first try to filter using the bbox of the segments - if (less_xy(A2,B1) - || less_xy(B2,A1)) + if (certainly(less_xy(A2,B1)) + || certainly(less_xy(B2,A1))) return S2S2_inter_info(false); switch(make_certain(compare_xy(A1,B1))) { @@ -229,14 +256,14 @@ do_intersect_with_info(const typename K::Segment_2 &seg1, case LARGER: switch(make_certain(compare_xy(A2,B2))) { case SMALLER: - return seg_seg_do_intersect_crossing(A1,A2,B1,B2, A1_id,A2_id,B1_id+2,B2_id+2, k, extra_test); + return seg_seg_do_intersect_crossing(A1,A2,B1,B2, A1_id,A2_id,B1_id+2,B2_id+2, k, extra_test, (seg1_is_left_to_right ? 0:2) + (seg2_is_left_to_right ? 0:1) ); case EQUAL: // A1 < B1 < B2 = A1 if (extra_test && k.collinear_2_object()(A1, A2, B1)) return S2S2_inter_info(B1_id+2, B2_id+2); // DI_MORE_INFO_TAG: A2==B2 but only B2 is reported return S2S2_inter_info(A2_id); // DI_MORE_INFO_TAG: A2==B2 but only A2 is reported case LARGER: - return seg_seg_do_intersect_contained(A1,A2,B1,B2, A1_id,A2_id,B1_id+2,B2_id+2, k, extra_test); + return seg_seg_do_intersect_contained(A1,A2,B1,B2, A1_id,A2_id,B1_id+2,B2_id+2, k, extra_test, (seg1_is_left_to_right ? 0:2) + (seg2_is_left_to_right ? 0:1)); default: CGAL_unreachable(); } @@ -275,14 +302,14 @@ do_intersect_with_info(const typename K::Segment_2 &seg1, case LARGER: switch(make_certain(compare_xy(B2,A2))) { case SMALLER: - return seg_seg_do_intersect_crossing(B1,B2,A1,A2, B1_id+2,B2_id+2,A1_id,A2_id, k, extra_test); + return seg_seg_do_intersect_crossing(B1,B2,A1,A2, B1_id+2,B2_id+2,A1_id,A2_id, k, extra_test, 4 + (seg1_is_left_to_right ? 0:1) + (seg2_is_left_to_right ? 0:2)); case EQUAL: // B1 < A1 < A2 = B2 if (extra_test && k.collinear_2_object()(B1, A1, B2)) return S2S2_inter_info(A1_id, A2_id); // DI_MORE_INFO_TAG: A2==B2 but only A2 is reported return S2S2_inter_info(A2_id); // DI_MORE_INFO_TAG: A2==B2 but only A2 is reported case LARGER: - return seg_seg_do_intersect_contained(B1,B2,A1,A2, B1_id+2,B2_id+2,A1_id,A2_id, k, extra_test); + return seg_seg_do_intersect_contained(B1,B2,A1,A2, B1_id+2,B2_id+2,A1_id,A2_id, k, extra_test, 4 + (seg1_is_left_to_right ? 0:1) + (seg2_is_left_to_right ? 0:2)); default: CGAL_unreachable(); } @@ -366,15 +393,22 @@ Segment_2_Segment_2_pair::intersection_type() const } // segments intersect in their interiors - typename K::FT s1_dx = _seg1->point(0).x() - _seg1->point(1).x(), - s1_dy = _seg1->point(0).y() - _seg1->point(1).y(), - s2_dx = _seg2->point(1).x() - _seg2->point(0).x(), - s2_dy = _seg2->point(1).y() - _seg2->point(0).y(), - lx = _seg2->point(1).x() - _seg1->point(1).x(), - ly = _seg2->point(1).y() - _seg1->point(1).y(); + int c = inter_info.config; + std::array pts = c < 4 + ? CGAL::make_array( _seg1->point(s2s2_id[c][0]), _seg1->point(s2s2_id[c][1]), + _seg2->point(s2s2_id[c][2]), _seg2->point(s2s2_id[c][3]) ) + : CGAL::make_array( _seg2->point(s2s2_id[c][2]), _seg2->point(s2s2_id[c][3]), + _seg1->point(s2s2_id[c][0]), _seg1->point(s2s2_id[c][1]) ); + + typename K::FT s1_dx = pts[0].x() - pts[1].x(), + s1_dy = pts[0].y() - pts[1].y(), + s2_dx = pts[3].x() - pts[2].x(), + s2_dy = pts[3].y() - pts[2].y(), + lx = pts[3].x() - pts[1].x(), + ly = pts[3].y() - pts[1].y(); typename K::FT alpha = (lx*s2_dy-ly*s2_dx)/(s1_dx*s2_dy-s1_dy*s2_dx); - _intersection_point = K().construct_barycenter_2_object()(_seg1->point(0), alpha, _seg1->point(1)); + _intersection_point = K().construct_barycenter_2_object()(pts[0], alpha, pts[1]); return _result; } From f7fa358585a2e32d881513d26f2f952e8eda3b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 15 Feb 2022 16:40:41 +0100 Subject: [PATCH 2/4] remove useless certainly will fail a few lines below anyway --- .../include/CGAL/Intersections_2/Segment_2_Segment_2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h b/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h index 19cfb0c0548..b444e4d0e40 100644 --- a/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h +++ b/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h @@ -242,8 +242,8 @@ do_intersect_with_info(const typename K::Segment_2 &seg1, typename K::Compare_xy_2 compare_xy; // first try to filter using the bbox of the segments - if (certainly(less_xy(A2,B1)) - || certainly(less_xy(B2,A1))) + if (less_xy(A2,B1) + || less_xy(B2,A1)) return S2S2_inter_info(false); switch(make_certain(compare_xy(A1,B1))) { From fe9b74dcd342bdad5280594c71263fbb00bec137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 15 Feb 2022 17:38:19 +0100 Subject: [PATCH 3/4] add determinism test --- .../Intersections_2/test_intersections_2.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Intersections_2/test/Intersections_2/test_intersections_2.cpp b/Intersections_2/test/Intersections_2/test_intersections_2.cpp index 40c55d26c66..434123fcdd2 100644 --- a/Intersections_2/test/Intersections_2/test_intersections_2.cpp +++ b/Intersections_2/test/Intersections_2/test_intersections_2.cpp @@ -481,6 +481,27 @@ struct Test check_intersection (S(p( -1,-1), p( -2,-2)), S(p(-2,-2), p(-1,-1)), S(p(-2,-2),p(-1,-1))); check_intersection (S(p( -2,-2), p( -1,-1)), S(p(-1,-1), p(-2,-2)), S(p(-2,-2),p(-1,-1))); check_intersection (S(p( -2,-2), p( -1,-1)), S(p(-2,-2), p(-1,-1)), S(p(-2,-2),p(-1,-1))); + + //check determinism of crossing point + P p1a(-122.37323046264295, 37.7435274415764); + P p1b(-122.3711959178425, 37.74348027376899); + P p2a(-122.37130249711004, 37.74203327688176); + P p2b(-122.3722247426892, 37.74401427059434); + std::set ds; + auto test = [&ds](S s1, S s2) + { + P i = boost::get

(*CGAL::intersection(s1,s2)); + ds.insert(CGAL::to_double(i.x())); ds.insert(CGAL::to_double(i.y())); + assert(ds.size()==2); + }; + test(S(p1a,p1b), S(p2a,p2b)); + test(S(p1a,p1b), S(p2b,p2a)); + test(S(p1b,p1a), S(p2b,p2a)); + test(S(p1b,p1a), S(p2a,p2b)); + test(S(p2a,p2b), S(p1a,p1b)); + test(S(p2b,p2a), S(p1a,p1b)); + test(S(p2b,p2a), S(p1b,p1a)); + test(S(p2a,p2b), S(p1b,p1a)); } void R_R() From 3c120f11b022ca1100564d9b23d11decdbd76cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 16 Feb 2022 12:16:46 +0100 Subject: [PATCH 4/4] add missing default --- .../include/CGAL/Intersections_2/Segment_2_Segment_2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h b/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h index b444e4d0e40..19d1b300232 100644 --- a/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h +++ b/Intersections_2/include/CGAL/Intersections_2/Segment_2_Segment_2.h @@ -154,7 +154,7 @@ seg_seg_do_intersect_crossing( const typename K::Point_2& p3, const typename K::Point_2& p4, const K& k) { - return seg_seg_do_intersect_crossing(p1,p2,p3,p4,0,0,0,0,k,false).inter; + return seg_seg_do_intersect_crossing(p1,p2,p3,p4,0,0,0,0,k,false,-1).inter; } @@ -215,7 +215,7 @@ seg_seg_do_intersect_contained( const typename K::Point_2& p3, const typename K::Point_2& p4, const K& k) { - return seg_seg_do_intersect_contained(p1,p2,p3,p4,0,0,0,0,k,false).inter; + return seg_seg_do_intersect_contained(p1,p2,p3,p4,0,0,0,0,k,false,-1).inter; } template