mirror of
https://github.com/boostorg/geometry.git
synced 2026-02-02 08:52:10 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user