[winding] Add winding_side_equal optimized for cartesian CS.

This commit is contained in:
Adam Wulkiewicz
2014-07-16 20:44:07 +02:00
parent 4966ff5359
commit 36da684609

View File

@@ -33,6 +33,78 @@ namespace boost { namespace geometry
namespace strategy { namespace within
{
// Fix for https://svn.boost.org/trac/boost/ticket/9628
// For floating point coordinates, the <1> coordinate of a point is compared
// with the segment's points using some EPS. If the coordinates are "equal"
// the sides are calculated. Therefore we can treat a segment as a long areal
// geometry having some width. There is a small ~triangular area somewhere
// between the segment's effective area and a segment's line used in sides
// calculation where the segment is on the one side of the line but on the
// other side of a segment (due to the width).
// For the s1 of a segment going NE the real side is RIGHT but the point may
// be detected as LEFT, like this:
// RIGHT
// ___----->
// ^ O Pt __ __
// EPS __ __
// v__ __ BUT DETECTED AS LEFT OF THIS LINE
// _____7
// _____/
// _____/
template <typename CSTag>
struct winding_side_equal
{
typedef typename strategy::side::services::default_strategy
<
CSTag
>::type strategy_side_type;
template <size_t D, typename Point, typename PointOfSegment>
static inline int apply(Point const& point,
PointOfSegment const& se,
int count)
{
// Create a vertical segment intersecting the original segment's endpoint
// equal to the point, with the derived direction (UP/DOWN).
// Set only the 2 first coordinates, the other ones are ignored
PointOfSegment ss1, ss2;
set<1-D>(ss1, get<1-D>(se));
set<1-D>(ss2, get<1-D>(se));
if ( count > 0 ) // UP
{
set<D>(ss1, 0);
set<D>(ss2, 1);
}
else // DOWN
{
set<D>(ss1, 1);
set<D>(ss2, 0);
}
// Check the side using this vertical segment
return strategy_side_type::apply(ss1, ss2, point);
}
};
// The optimization for cartesian
template <>
struct winding_side_equal<cartesian_tag>
{
template <size_t D, typename Point, typename PointOfSegment>
static inline int apply(Point const& point,
PointOfSegment const& se,
int count)
{
return math::equals(get<1-D>(point), get<1-D>(se)) ?
0 :
get<1-D>(point) < get<1-D>(se) ?
// assuming count is equal to 1 or -1
count : // ( count > 0 ? 1 : -1) :
-count; // ( count > 0 ? -1 : 1) ;
}
};
/*!
\brief Within detection using winding rule
\ingroup strategies
@@ -137,56 +209,6 @@ class winding
: 0;
}
// Fix for https://svn.boost.org/trac/boost/ticket/9628
// For floating point coordinates, the <1> coordinate of a point is compared
// with the segment's points using some EPS. If the coordinates are "equal"
// the sides are calculated. Therefore we can treat a segment as a long areal
// geometry having some width. There is a small ~triangular area somewhere
// between the segment's effective area and a segment's line used in sides
// calculation where the segment is on the one side of the line but on the
// other side of a segment (due to the width).
// For the s1 of a segment going NE the real side is RIGHT but the point may
// be detected as LEFT, like this:
// RIGHT
// ___----->
// ^ O Pt __ __
// EPS __ __
// v__ __ BUT DETECTED AS LEFT OF THIS LINE
// _____7
// _____/
// _____/
template <size_t D>
static inline int side_equal(Point const& point,
PointOfSegment const& se,
int count)
{
// TODO: possible optimization of cartesian CS
// math::equals(p<1-D>, se<1-D>) ?
// 0 :
// p<1-D> < se<1-D> ?
// (UP ? LEFT : RIGHT) :
// (UP ? RIGHT : LEFT) ;
// Create a vertical segment intersecting the original segment's endpoint
// equal to the point, with the derived direction (UP/DOWN).
// Set only the 2 first coordinates, the other ones are ignored
PointOfSegment ss1, ss2;
set<1-D>(ss1, get<1-D>(se));
set<1-D>(ss2, get<1-D>(se));
if ( count > 0 ) // UP
{
set<D>(ss1, 0);
set<D>(ss2, 1);
}
else // DOWN
{
set<D>(ss1, 1);
set<D>(ss2, 0);
}
// Check the side using this vertical segment
return strategy_side_type::apply(ss1, ss2, point);
}
public :
@@ -209,7 +231,8 @@ public :
int side = 0;
if ( count == 1 || count == -1 )
{
side = side_equal<1>(point, eq1 ? s1 : s2, count);
side = winding_side_equal<typename cs_tag<Point>::type>
::template apply<1>(point, eq1 ? s1 : s2, count);
}
else
{