// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2014, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace distance { template < typename Segment, typename Box, typename Strategy, bool UsePointBoxStrategy = false > class segment_to_box_2D_generic { private: typedef typename point_type::type segment_point; typedef typename point_type::type box_point; typedef typename strategy::distance::services::comparable_type < Strategy >::type comparable_strategy; typedef detail::closest_feature::point_to_point_range < segment_point, std::vector, open, comparable_strategy > point_to_point_range; typedef typename strategy::distance::services::return_type < comparable_strategy, segment_point, box_point >::type comparable_return_type; public: typedef typename strategy::distance::services::return_type < Strategy, segment_point, box_point >::type return_type; static inline return_type apply(Segment const& segment, Box const& box, Strategy const& strategy, bool check_intersection = true) { if (check_intersection && geometry::intersects(segment, box)) { return 0; } comparable_strategy cstrategy = strategy::distance::services::get_comparable < Strategy >::apply(strategy); // get segment points segment_point p[2]; detail::assign_point_from_index<0>(segment, p[0]); detail::assign_point_from_index<1>(segment, p[1]); // get box points std::vector box_points(4); detail::assign_box_corners_oriented(box, box_points); comparable_return_type cd[6]; for (unsigned int i = 0; i < 4; ++i) { cd[i] = cstrategy.apply(box_points[i], p[0], p[1]); } std::pair < typename std::vector::const_iterator, typename std::vector::const_iterator > bit_min[2]; bit_min[0] = point_to_point_range::apply(p[0], box_points.begin(), box_points.end(), cstrategy, cd[4]); bit_min[1] = point_to_point_range::apply(p[1], box_points.begin(), box_points.end(), cstrategy, cd[5]); unsigned int imin = 0; for (unsigned int i = 1; i < 6; ++i) { if (cd[i] < cd[imin]) { imin = i; } } if (BOOST_GEOMETRY_CONDITION(is_comparable::value)) { return cd[imin]; } if (imin < 4) { return strategy.apply(box_points[imin], p[0], p[1]); } else { unsigned int bimin = imin - 4; return strategy.apply(p[bimin], *bit_min[bimin].first, *bit_min[bimin].second); } } }; template < typename Segment, typename Box, typename Strategy > class segment_to_box_2D_generic { private: typedef typename point_type::type segment_point; typedef typename point_type::type box_point; typedef typename strategy::distance::services::comparable_type < Strategy >::type comparable_strategy; typedef typename strategy::distance::services::return_type < comparable_strategy, segment_point, box_point >::type comparable_return_type; typedef typename detail::distance::default_strategy < segment_point, Box >::type point_box_strategy; typedef typename strategy::distance::services::comparable_type < point_box_strategy >::type point_box_comparable_strategy; public: typedef typename strategy::distance::services::return_type < Strategy, segment_point, box_point >::type return_type; static inline return_type apply(Segment const& segment, Box const& box, Strategy const& strategy, bool check_intersection = true) { if (check_intersection && geometry::intersects(segment, box)) { return 0; } comparable_strategy cstrategy = strategy::distance::services::get_comparable < Strategy >::apply(strategy); boost::ignore_unused(cstrategy); // get segment points segment_point p[2]; detail::assign_point_from_index<0>(segment, p[0]); detail::assign_point_from_index<1>(segment, p[1]); // get box points std::vector box_points(4); detail::assign_box_corners_oriented(box, box_points); comparable_return_type cd[6]; for (unsigned int i = 0; i < 4; ++i) { cd[i] = cstrategy.apply(box_points[i], p[0], p[1]); } point_box_comparable_strategy pb_cstrategy; boost::ignore_unused(pb_cstrategy); cd[4] = pb_cstrategy.apply(p[0], box); cd[5] = pb_cstrategy.apply(p[1], box); unsigned int imin = 0; for (unsigned int i = 1; i < 6; ++i) { if (cd[i] < cd[imin]) { imin = i; } } if (is_comparable::value) { return cd[imin]; } if (imin < 4) { strategy.apply(box_points[imin], p[0], p[1]); } else { return point_box_strategy().apply(p[imin - 4], box); } } }; template < typename ReturnType, typename SegmentPoint, typename BoxPoint, typename PPStrategy, typename PSStrategy > class segment_to_box_2D { private: template struct cast_to_result { template static inline Result apply(T const& t) { return boost::numeric_cast(t); } }; template struct compare_less_equal { typedef compare_less_equal other; template inline bool operator()(T1 const& t1, T2 const& t2) const { return std::less_equal()(cast_to_result::apply(t1), cast_to_result::apply(t2)); } }; template struct compare_less_equal { typedef compare_less_equal other; template inline bool operator()(T1 const& t1, T2 const& t2) const { return std::greater_equal()(cast_to_result::apply(t1), cast_to_result::apply(t2)); } }; template struct other_compare { typedef typename LessEqual::other type; }; // it is assumed here that p0 lies to the right of the box (so the // entire segment lies to the right of the box) template struct right_of_box { static inline ReturnType apply(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& bottom_right, BoxPoint const& top_right, PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) { boost::ignore_unused(pp_strategy, ps_strategy); // the implementation below is written for non-negative slope // segments // // for negative slope segments swap the roles of bottom_right // and top_right and use greater_equal instead of less_equal. typedef cast_to_result cast; LessEqual less_equal; if (less_equal(geometry::get<1>(top_right), geometry::get<1>(p0))) { // closest box point is the top-right corner return cast::apply(pp_strategy.apply(p0, top_right)); } else if (less_equal(geometry::get<1>(bottom_right), geometry::get<1>(p0))) { // distance is realized between p0 and right-most // segment of box ReturnType diff = cast::apply(geometry::get<0>(p0)) - cast::apply(geometry::get<0>(bottom_right)); return strategy::distance::services::result_from_distance < PSStrategy, BoxPoint, SegmentPoint >::apply(ps_strategy, math::abs(diff)); } else { // distance is realized between the bottom-right // corner of the box and the segment return cast::apply(ps_strategy.apply(bottom_right, p0, p1)); } } }; // it is assumed here that p0 lies above the box (so the // entire segment lies above the box) template struct above_of_box { static inline ReturnType apply(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& top_left, PSStrategy const& ps_strategy) { boost::ignore_unused(ps_strategy); // the segment lies above the box typedef cast_to_result cast; LessEqual less_equal; // p0 is above the upper segment of the box // (and inside its band) if (less_equal(geometry::get<0>(top_left), geometry::get<0>(p0))) { ReturnType diff = cast::apply(geometry::get<1>(p0)) - cast::apply(geometry::get<1>(top_left)); return strategy::distance::services::result_from_distance < PSStrategy, SegmentPoint, BoxPoint >::apply(ps_strategy, math::abs(diff)); } // p0 is to the left of the box, but p1 is above the box // in this case the distance is realized between the // top-left corner of the box and the segment return cast::apply(ps_strategy.apply(top_left, p0, p1)); } }; template struct check_right_left_of_box { static inline bool apply(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& top_left, BoxPoint const& top_right, BoxPoint const& bottom_left, BoxPoint const& bottom_right, PPStrategy const& pp_strategy, PSStrategy const& ps_strategy, ReturnType& result) { // p0 lies to the right of the box if (geometry::get<0>(p0) >= geometry::get<0>(top_right)) { result = right_of_box < LessEqual >::apply(p0, p1, bottom_right, top_right, pp_strategy, ps_strategy); return true; } // p1 lies to the left of the box if (geometry::get<0>(p1) <= geometry::get<0>(bottom_left)) { result = right_of_box < typename other_compare::type >::apply(p1, p0, top_left, bottom_left, pp_strategy, ps_strategy); return true; } return false; } }; template struct check_above_below_of_box { static inline bool apply(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& top_left, BoxPoint const& top_right, BoxPoint const& bottom_left, BoxPoint const& bottom_right, PSStrategy const& ps_strategy, ReturnType& result) { // the segment lies below the box if (geometry::get<1>(p1) < geometry::get<1>(bottom_left)) { result = above_of_box < typename other_compare::type >::apply(p1, p0, bottom_right, ps_strategy); return true; } // the segment lies above the box if (geometry::get<1>(p0) > geometry::get<1>(top_right)) { result = above_of_box < LessEqual >::apply(p0, p1, top_left, ps_strategy); return true; } return false; } }; struct check_generic_position { static inline bool apply(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& bottom_left0, BoxPoint const& top_right0, BoxPoint const& bottom_left1, BoxPoint const& top_right1, BoxPoint const& corner1, BoxPoint const& corner2, PSStrategy const& ps_strategy, ReturnType& result) { typedef cast_to_result cast; ReturnType diff0 = cast::apply(geometry::get<0>(p1)) - cast::apply(geometry::get<0>(p0)); ReturnType t_min0 = cast::apply(geometry::get<0>(bottom_left0)) - cast::apply(geometry::get<0>(p0)); ReturnType t_max0 = cast::apply(geometry::get<0>(top_right0)) - cast::apply(geometry::get<0>(p0)); ReturnType diff1 = cast::apply(geometry::get<1>(p1)) - cast::apply(geometry::get<1>(p0)); ReturnType t_min1 = cast::apply(geometry::get<1>(bottom_left1)) - cast::apply(geometry::get<1>(p0)); ReturnType t_max1 = cast::apply(geometry::get<1>(top_right1)) - cast::apply(geometry::get<1>(p0)); if (diff1 < 0) { diff1 = -diff1; t_min1 = -t_min1; t_max1 = -t_max1; } // t_min0 > t_max1 if (t_min0 * diff1 > t_max1 * diff0) { result = cast::apply(ps_strategy.apply(corner1, p0, p1)); return true; } // t_max0 < t_min1 if (t_max0 * diff1 < t_min1 * diff0) { result = cast::apply(ps_strategy.apply(corner2, p0, p1)); return true; } return false; } }; static inline ReturnType non_negative_slope_segment(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& top_left, BoxPoint const& top_right, BoxPoint const& bottom_left, BoxPoint const& bottom_right, PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) { typedef compare_less_equal less_equal; // assert that the segment has non-negative slope BOOST_ASSERT( ( math::equals(geometry::get<0>(p0), geometry::get<0>(p1)) && geometry::get<1>(p0) < geometry::get<1>(p1)) || ( geometry::get<0>(p0) < geometry::get<0>(p1) && geometry::get<1>(p0) <= geometry::get<1>(p1) ) ); ReturnType result(0); if (check_right_left_of_box < less_equal >::apply(p0, p1, top_left, top_right, bottom_left, bottom_right, pp_strategy, ps_strategy, result)) { return result; } if (check_above_below_of_box < less_equal >::apply(p0, p1, top_left, top_right, bottom_left, bottom_right, ps_strategy, result)) { return result; } if (check_generic_position::apply(p0, p1, bottom_left, top_right, bottom_left, top_right, top_left, bottom_right, ps_strategy, result)) { return result; } // in all other cases the box and segment intersect, so return 0 return result; } static inline ReturnType negative_slope_segment(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& top_left, BoxPoint const& top_right, BoxPoint const& bottom_left, BoxPoint const& bottom_right, PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) { typedef compare_less_equal greater_equal; // assert that the segment has negative slope BOOST_ASSERT( geometry::get<0>(p0) < geometry::get<0>(p1) && geometry::get<1>(p0) > geometry::get<1>(p1) ); ReturnType result(0); if (check_right_left_of_box < greater_equal >::apply(p0, p1, bottom_left, bottom_right, top_left, top_right, pp_strategy, ps_strategy, result)) { return result; } if (check_above_below_of_box < greater_equal >::apply(p1, p0, top_right, top_left, bottom_right, bottom_left, ps_strategy, result)) { return result; } if (check_generic_position::apply(p0, p1, bottom_left, top_right, top_right, bottom_left, bottom_left, top_right, ps_strategy, result)) { return result; } // in all other cases the box and segment intersect, so return 0 return result; } public: static inline ReturnType apply(SegmentPoint const& p0, SegmentPoint const& p1, BoxPoint const& top_left, BoxPoint const& top_right, BoxPoint const& bottom_left, BoxPoint const& bottom_right, PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) { BOOST_ASSERT( geometry::less()(p0, p1) ); if (geometry::get<0>(p0) < geometry::get<0>(p1) && geometry::get<1>(p0) > geometry::get<1>(p1)) { return negative_slope_segment(p0, p1, top_left, top_right, bottom_left, bottom_right, pp_strategy, ps_strategy); } return non_negative_slope_segment(p0, p1, top_left, top_right, bottom_left, bottom_right, pp_strategy, ps_strategy); } }; //========================================================================= template < typename Segment, typename Box, typename std::size_t Dimension, typename PPStrategy, typename PSStrategy > class segment_to_box : not_implemented {}; template < typename Segment, typename Box, typename PPStrategy, typename PSStrategy > class segment_to_box { private: typedef typename point_type::type segment_point; typedef typename point_type::type box_point; typedef typename strategy::distance::services::comparable_type < PPStrategy >::type pp_comparable_strategy; typedef typename strategy::distance::services::comparable_type < PSStrategy >::type ps_comparable_strategy; typedef typename strategy::distance::services::return_type < ps_comparable_strategy, segment_point, box_point >::type comparable_return_type; public: typedef typename strategy::distance::services::return_type < PSStrategy, segment_point, box_point >::type return_type; static inline return_type apply(Segment const& segment, Box const& box, PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) { segment_point p[2]; detail::assign_point_from_index<0>(segment, p[0]); detail::assign_point_from_index<1>(segment, p[1]); if (geometry::equals(p[0], p[1])) { typedef typename boost::mpl::if_ < boost::is_same < ps_comparable_strategy, PSStrategy >, typename strategy::distance::services::comparable_type < typename detail::distance::default_strategy < segment_point, Box >::type >::type, typename detail::distance::default_strategy < segment_point, Box >::type >::type point_box_strategy_type; return dispatch::distance < segment_point, Box, point_box_strategy_type >::apply(p[0], box, point_box_strategy_type()); } box_point top_left, top_right, bottom_left, bottom_right; detail::assign_box_corners(box, bottom_left, bottom_right, top_left, top_right); if (geometry::less()(p[0], p[1])) { return segment_to_box_2D < return_type, segment_point, box_point, PPStrategy, PSStrategy >::apply(p[0], p[1], top_left, top_right, bottom_left, bottom_right, pp_strategy, ps_strategy); } else { return segment_to_box_2D < return_type, segment_point, box_point, PPStrategy, PSStrategy >::apply(p[1], p[0], top_left, top_right, bottom_left, bottom_right, pp_strategy, ps_strategy); } } }; }} // namespace detail::distance #endif // DOXYGEN_NO_DETAIL #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { template struct distance < Segment, Box, Strategy, segment_tag, box_tag, strategy_tag_distance_point_segment, false > { typedef typename strategy::distance::services::return_type < Strategy, typename point_type::type, typename point_type::type >::type return_type; static inline return_type apply(Segment const& segment, Box const& box, Strategy const& strategy) { assert_dimension_equal(); typedef typename boost::mpl::if_ < boost::is_same < typename strategy::distance::services::comparable_type < Strategy >::type, Strategy >, typename strategy::distance::services::comparable_type < typename detail::distance::default_strategy < typename point_type::type, typename point_type::type >::type >::type, typename detail::distance::default_strategy < typename point_type::type, typename point_type::type >::type >::type pp_strategy_type; return detail::distance::segment_to_box < Segment, Box, dimension::value, pp_strategy_type, Strategy >::apply(segment, box, pp_strategy_type(), strategy); } }; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP