diff --git a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp index 4ed54c77e..e6161b677 100644 --- a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp +++ b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp @@ -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 +struct winding_side_equal +{ + typedef typename strategy::side::services::default_strategy + < + CSTag + >::type strategy_side_type; + + template + 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(ss1, 0); + set(ss2, 1); + } + else // DOWN + { + set(ss1, 1); + set(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 +{ + template + 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 - 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(ss1, 0); - set(ss2, 1); - } - else // DOWN - { - set(ss1, 1); - set(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::type> + ::template apply<1>(point, eq1 ? s1 : s2, count); } else {