intersection calculation: fix behaviour in rare cases where IP is taken from segment

A but segment B is much shorter, and IP is also at end-point of segment B.
The arbitrary decision (robust_ra < robust_rb) is changed into another
decision, based on closeness to end-points and on segment length, which leads
to more precise results
This commit is contained in:
barendgehrels
2015-06-21 22:50:40 +02:00
parent 452f092e8e
commit ed103f777e
2 changed files with 50 additions and 7 deletions

View File

@@ -82,7 +82,6 @@ struct segments_intersection_points
>(numerator * dy_promoted / denominator));
}
template
<
typename Segment1,
@@ -96,7 +95,33 @@ struct segments_intersection_points
return_type result;
result.count = 1;
if (sinfo.robust_ra < sinfo.robust_rb)
bool use_a = true;
// Prefer one segment if one is on or near an endpoint
bool const a_near_end = sinfo.robust_ra.near_end();
bool const b_near_end = sinfo.robust_rb.near_end();
if (a_near_end && ! b_near_end)
{
use_a = true;
}
else if (b_near_end && ! a_near_end)
{
use_a = false;
}
else
{
// Prefer shorter segment
typedef typename SegmentIntersectionInfo::promoted_type ptype;
ptype const len_a = sinfo.dx_a * sinfo.dx_a + sinfo.dy_a * sinfo.dy_a;
ptype const len_b = sinfo.dx_b * sinfo.dx_b + sinfo.dy_b * sinfo.dy_b;
if (len_b < len_a)
{
use_a = false;
}
// else use_a is true but was already assigned like that
}
if (use_a)
{
assign(result.intersections[0], s1, sinfo.robust_ra,
sinfo.dx_a, sinfo.dy_a);

View File

@@ -139,14 +139,12 @@ public :
m_denominator = -m_denominator;
}
typedef typename promote_floating_point<Type>::type num_type;
static const num_type scale = 1000000.0;
m_approximation =
m_denominator == 0 ? 0
: boost::numeric_cast<double>
(
boost::numeric_cast<num_type>(m_numerator) * scale
/ boost::numeric_cast<num_type>(m_denominator)
boost::numeric_cast<fp_type>(m_numerator) * scale()
/ boost::numeric_cast<fp_type>(m_denominator)
);
}
@@ -178,6 +176,18 @@ public :
return m_numerator > m_denominator;
}
inline bool near_end() const
{
if (left() || right())
{
return false;
}
static fp_type const small_part_of_scale = scale() / 100.0;
return m_approximation < small_part_of_scale
|| m_approximation > scale() - small_part_of_scale;
}
inline bool close_to(thistype const& other) const
{
return geometry::math::abs(m_approximation - other.m_approximation) < 2;
@@ -222,6 +232,8 @@ public :
private :
typedef typename promote_floating_point<Type>::type fp_type;
Type m_numerator;
Type m_denominator;
@@ -230,7 +242,13 @@ private :
// Boost.Rational is used if the approximations are close.
// Reason: performance, Boost.Rational does a GCD by default and also the
// comparisons contain while-loops.
double m_approximation;
fp_type m_approximation;
static inline fp_type scale()
{
return 1000000.0;
}
};