make the crossing point computation deterministic

This commit is contained in:
Sébastien Loriot 2022-02-15 15:47:48 +01:00 committed by Laurent Rineau
parent fb764adef8
commit f6f2b28b0e
1 changed files with 54 additions and 20 deletions

View File

@ -34,6 +34,22 @@ namespace Intersections {
namespace internal { 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 // struct used to report the combinaric of the intersection
// of 2 2D segments. // of 2 2D segments.
// More information could be gathered if exposed in do_intersect. // More information could be gathered if exposed in do_intersect.
@ -43,9 +59,20 @@ struct S2S2_inter_info
bool inter = false; bool inter = false;
bool dim = 0; bool dim = 0;
std::array<int, 2> pt_ids = {-1,-1}; std::array<int, 2> 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) : inter(inter)
, config(c)
{} {}
// intersection is an input endpoint // 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& p1, const typename K::Point_2& p2,
const typename K::Point_2& p3, const typename K::Point_2& p4, const typename K::Point_2& p3, const typename K::Point_2& p4,
int /* i1 */, int i2, int i3, int /* i4 */, 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))) { switch (make_certain(k.orientation_2_object()(p1,p2,p3))) {
case LEFT_TURN: case LEFT_TURN:
@ -89,7 +116,7 @@ seg_seg_do_intersect_crossing(
case RIGHT_TURN: case RIGHT_TURN:
return S2S2_inter_info(false); return S2S2_inter_info(false);
case LEFT_TURN: case LEFT_TURN:
return S2S2_inter_info(true); return S2S2_inter_info(true, config);
default: default:
CGAL_unreachable(); CGAL_unreachable();
} }
@ -101,7 +128,7 @@ seg_seg_do_intersect_crossing(
case COLLINEAR: case COLLINEAR:
return S2S2_inter_info(i2); return S2S2_inter_info(i2);
case RIGHT_TURN: case RIGHT_TURN:
return S2S2_inter_info(true); return S2S2_inter_info(true, config);
case LEFT_TURN: case LEFT_TURN:
return S2S2_inter_info(false); return S2S2_inter_info(false);
default: 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& p1, const typename K::Point_2& p2,
const typename K::Point_2& p3, const typename K::Point_2& p4, const typename K::Point_2& p3, const typename K::Point_2& p4,
int /* i1 */, int /* i2 */, int i3, int i4, 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))) { switch (make_certain(k.orientation_2_object()(p1,p2,p3))) {
case LEFT_TURN: case LEFT_TURN:
@ -148,7 +175,7 @@ seg_seg_do_intersect_contained(
case COLLINEAR: case COLLINEAR:
return S2S2_inter_info(i4); return S2S2_inter_info(i4);
case RIGHT_TURN: case RIGHT_TURN:
return S2S2_inter_info(true); return S2S2_inter_info(true, config);
case LEFT_TURN: case LEFT_TURN:
return S2S2_inter_info(false); return S2S2_inter_info(false);
default: default:
@ -164,7 +191,7 @@ seg_seg_do_intersect_contained(
case RIGHT_TURN: case RIGHT_TURN:
return S2S2_inter_info(false); return S2S2_inter_info(false);
case LEFT_TURN: case LEFT_TURN:
return S2S2_inter_info(true); return S2S2_inter_info(true, config);
default: default:
CGAL_unreachable(); CGAL_unreachable();
} }
@ -215,8 +242,8 @@ do_intersect_with_info(const typename K::Segment_2 &seg1,
typename K::Compare_xy_2 compare_xy; typename K::Compare_xy_2 compare_xy;
// first try to filter using the bbox of the segments // first try to filter using the bbox of the segments
if (less_xy(A2,B1) if (certainly(less_xy(A2,B1))
|| less_xy(B2,A1)) || certainly(less_xy(B2,A1)))
return S2S2_inter_info(false); return S2S2_inter_info(false);
switch(make_certain(compare_xy(A1,B1))) { switch(make_certain(compare_xy(A1,B1))) {
@ -229,14 +256,14 @@ do_intersect_with_info(const typename K::Segment_2 &seg1,
case LARGER: case LARGER:
switch(make_certain(compare_xy(A2,B2))) { switch(make_certain(compare_xy(A2,B2))) {
case SMALLER: 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: case EQUAL:
// A1 < B1 < B2 = A1 // A1 < B1 < B2 = A1
if (extra_test && k.collinear_2_object()(A1, A2, B1)) 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(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 return S2S2_inter_info(A2_id); // DI_MORE_INFO_TAG: A2==B2 but only A2 is reported
case LARGER: 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: default:
CGAL_unreachable(); CGAL_unreachable();
} }
@ -275,14 +302,14 @@ do_intersect_with_info(const typename K::Segment_2 &seg1,
case LARGER: case LARGER:
switch(make_certain(compare_xy(B2,A2))) { switch(make_certain(compare_xy(B2,A2))) {
case SMALLER: 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: case EQUAL:
// B1 < A1 < A2 = B2 // B1 < A1 < A2 = B2
if (extra_test && k.collinear_2_object()(B1, A1, 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(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 return S2S2_inter_info(A2_id); // DI_MORE_INFO_TAG: A2==B2 but only A2 is reported
case LARGER: 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: default:
CGAL_unreachable(); CGAL_unreachable();
} }
@ -366,15 +393,22 @@ Segment_2_Segment_2_pair<K>::intersection_type() const
} }
// segments intersect in their interiors // segments intersect in their interiors
typename K::FT s1_dx = _seg1->point(0).x() - _seg1->point(1).x(), int c = inter_info.config;
s1_dy = _seg1->point(0).y() - _seg1->point(1).y(), std::array<typename K::Point_2, 4> pts = c < 4
s2_dx = _seg2->point(1).x() - _seg2->point(0).x(), ? CGAL::make_array( _seg1->point(s2s2_id[c][0]), _seg1->point(s2s2_id[c][1]),
s2_dy = _seg2->point(1).y() - _seg2->point(0).y(), _seg2->point(s2s2_id[c][2]), _seg2->point(s2s2_id[c][3]) )
lx = _seg2->point(1).x() - _seg1->point(1).x(), : CGAL::make_array( _seg2->point(s2s2_id[c][2]), _seg2->point(s2s2_id[c][3]),
ly = _seg2->point(1).y() - _seg1->point(1).y(); _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); 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; return _result;
} }