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 {
// 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<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)
, 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<K>::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<typename K::Point_2, 4> 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;
}