summaryrefslogtreecommitdiff
path: root/boost/geometry/strategies/cartesian/cart_intersect.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/strategies/cartesian/cart_intersect.hpp')
-rw-r--r--boost/geometry/strategies/cartesian/cart_intersect.hpp862
1 files changed, 244 insertions, 618 deletions
diff --git a/boost/geometry/strategies/cartesian/cart_intersect.hpp b/boost/geometry/strategies/cartesian/cart_intersect.hpp
index ea92cf37b0..66af2d2e9c 100644
--- a/boost/geometry/strategies/cartesian/cart_intersect.hpp
+++ b/boost/geometry/strategies/cartesian/cart_intersect.hpp
@@ -1,6 +1,7 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
+// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
@@ -18,6 +19,9 @@
#include <boost/geometry/arithmetic/determinant.hpp>
#include <boost/geometry/algorithms/detail/assign_values.hpp>
+#include <boost/geometry/algorithms/detail/assign_indexed_point.hpp>
+#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
+#include <boost/geometry/algorithms/detail/recalculate.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
@@ -27,59 +31,24 @@
#include <boost/geometry/strategies/cartesian/side_by_triangle.hpp>
#include <boost/geometry/strategies/side_info.hpp>
+#include <boost/geometry/strategies/intersection_result.hpp>
-
-namespace boost { namespace geometry
-{
+#include <boost/geometry/policies/robustness/robust_point_type.hpp>
+#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
-namespace strategy { namespace intersection
-{
+#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
+# include <boost/geometry/io/wkt/write.hpp>
+#endif
-#ifndef DOXYGEN_NO_DETAIL
-namespace detail
+namespace boost { namespace geometry
{
-template <std::size_t Dimension, typename Segment, typename T>
-static inline void segment_arrange(Segment const& s, T& s_1, T& s_2, bool& swapped)
-{
- s_1 = get<0, Dimension>(s);
- s_2 = get<1, Dimension>(s);
- if (s_1 > s_2)
- {
- std::swap(s_1, s_2);
- swapped = true;
- }
-}
-template <std::size_t Index, typename Segment>
-inline typename geometry::point_type<Segment>::type get_from_index(
- Segment const& segment)
+namespace strategy { namespace intersection
{
- typedef typename geometry::point_type<Segment>::type point_type;
- point_type point;
- geometry::detail::assign::assign_point_from_index
- <
- Segment, point_type, Index, 0, dimension<Segment>::type::value
- >::apply(segment, point);
- return point;
-}
-}
-#endif
-
-/***
-template <typename T>
-inline std::string rdebug(T const& value)
-{
- if (math::equals(value, 0)) return "'0'";
- if (math::equals(value, 1)) return "'1'";
- if (value < 0) return "<0";
- if (value > 1) return ">1";
- return "<0..1>";
-}
-***/
/*!
\see http://mathworld.wolfram.com/Line-LineIntersection.html
@@ -88,650 +57,307 @@ template <typename Policy, typename CalculationType = void>
struct relate_cartesian_segments
{
typedef typename Policy::return_type return_type;
- typedef typename Policy::segment_type1 segment_type1;
- typedef typename Policy::segment_type2 segment_type2;
-
- //typedef typename point_type<segment_type1>::type point_type;
- //BOOST_CONCEPT_ASSERT( (concept::Point<point_type>) );
-
- BOOST_CONCEPT_ASSERT( (concept::ConstSegment<segment_type1>) );
- BOOST_CONCEPT_ASSERT( (concept::ConstSegment<segment_type2>) );
-
- typedef typename select_calculation_type
- <segment_type1, segment_type2, CalculationType>::type coordinate_type;
- /// Relate segments a and b
- static inline return_type apply(segment_type1 const& a, segment_type2 const& b)
+ template <typename D, typename W, typename ResultType>
+ static inline void cramers_rule(D const& dx_a, D const& dy_a,
+ D const& dx_b, D const& dy_b, W const& wx, W const& wy,
+ // out:
+ ResultType& d, ResultType& da)
{
- coordinate_type const dx_a = get<1, 0>(a) - get<0, 0>(a); // distance in x-dir
- coordinate_type const dx_b = get<1, 0>(b) - get<0, 0>(b);
- coordinate_type const dy_a = get<1, 1>(a) - get<0, 1>(a); // distance in y-dir
- coordinate_type const dy_b = get<1, 1>(b) - get<0, 1>(b);
- return apply(a, b, dx_a, dy_a, dx_b, dy_b);
+ // Cramers rule
+ d = geometry::detail::determinant<ResultType>(dx_a, dy_a, dx_b, dy_b);
+ da = geometry::detail::determinant<ResultType>(dx_b, dy_b, wx, wy);
+ // Ratio is da/d , collinear if d == 0, intersecting if 0 <= r <= 1
+ // IntersectionPoint = (x1 + r * dx_a, y1 + r * dy_a)
}
- // Relate segments a and b using precalculated differences.
- // This can save two or four subtractions in many cases
- static inline return_type apply(segment_type1 const& a, segment_type2 const& b,
- coordinate_type const& dx_a, coordinate_type const& dy_a,
- coordinate_type const& dx_b, coordinate_type const& dy_b)
+ // Relate segments a and b
+ template <typename Segment1, typename Segment2, typename RobustPolicy>
+ static inline return_type apply(Segment1 const& a, Segment2 const& b,
+ RobustPolicy const& robust_policy)
{
- typedef side::side_by_triangle<coordinate_type> side;
- side_info sides;
-
- bool collinear_use_first = math::abs(dx_a) + math::abs(dx_b) >= math::abs(dy_a) + math::abs(dy_b);
-
- sides.set<0>
- (
- side::apply(detail::get_from_index<0>(b)
- , detail::get_from_index<1>(b)
- , detail::get_from_index<0>(a)),
- side::apply(detail::get_from_index<0>(b)
- , detail::get_from_index<1>(b)
- , detail::get_from_index<1>(a))
- );
- sides.set<1>
- (
- side::apply(detail::get_from_index<0>(a)
- , detail::get_from_index<1>(a)
- , detail::get_from_index<0>(b)),
- side::apply(detail::get_from_index<0>(a)
- , detail::get_from_index<1>(a)
- , detail::get_from_index<1>(b))
- );
+ // type them all as in Segment1 - TODO reconsider this, most precise?
+ typedef typename geometry::point_type<Segment1>::type point_type;
- bool collinear = sides.collinear();
+ typedef typename geometry::robust_point_type
+ <
+ point_type, RobustPolicy
+ >::type robust_point_type;
- robustness_verify_collinear(a, b, sides, collinear);
- robustness_verify_meeting(a, b, sides, collinear, collinear_use_first);
+ point_type a0, a1, b0, b1;
+ robust_point_type a0_rob, a1_rob, b0_rob, b1_rob;
- if (sides.same<0>() || sides.same<1>())
- {
- // Both points are at same side of other segment, we can leave
- if (robustness_verify_same_side(a, b, sides))
- {
- return Policy::disjoint();
- }
- }
+ detail::assign_point_from_index<0>(a, a0);
+ detail::assign_point_from_index<1>(a, a1);
+ detail::assign_point_from_index<0>(b, b0);
+ detail::assign_point_from_index<1>(b, b1);
- // Degenerate cases: segments of single point, lying on other segment, non disjoint
- coordinate_type const zero = 0;
- if (math::equals(dx_a, zero) && math::equals(dy_a, zero))
- {
- return Policy::degenerate(a, true);
- }
- if (math::equals(dx_b, zero) && math::equals(dy_b, zero))
- {
- return Policy::degenerate(b, false);
- }
+ geometry::recalculate(a0_rob, a0, robust_policy);
+ geometry::recalculate(a1_rob, a1, robust_policy);
+ geometry::recalculate(b0_rob, b0, robust_policy);
+ geometry::recalculate(b1_rob, b1, robust_policy);
- typedef typename select_most_precise
- <
- coordinate_type, double
- >::type promoted_type;
+ return apply(a, b, robust_policy, a0_rob, a1_rob, b0_rob, b1_rob);
+ }
- // r: ratio 0-1 where intersection divides A/B
- // (only calculated for non-collinear segments)
- promoted_type r;
- if (! collinear)
- {
- // Calculate determinants - Cramers rule
- coordinate_type const wx = get<0, 0>(a) - get<0, 0>(b);
- coordinate_type const wy = get<0, 1>(a) - get<0, 1>(b);
- coordinate_type const d = geometry::detail::determinant<coordinate_type>(dx_a, dy_a, dx_b, dy_b);
- coordinate_type const da = geometry::detail::determinant<coordinate_type>(dx_b, dy_b, wx, wy);
-
- coordinate_type const zero = coordinate_type();
- if (math::equals(d, zero))
- {
- // This is still a collinear case (because of FP imprecision this can occur here)
- // sides.debug();
- sides.set<0>(0,0);
- sides.set<1>(0,0);
- collinear = true;
- }
- else
- {
- r = promoted_type(da) / promoted_type(d);
+ // The main entry-routine, calculating intersections of segments a / b
+ template <typename Segment1, typename Segment2, typename RobustPolicy, typename RobustPoint>
+ static inline return_type apply(Segment1 const& a, Segment2 const& b,
+ RobustPolicy const& robust_policy,
+ RobustPoint const& robust_a1, RobustPoint const& robust_a2,
+ RobustPoint const& robust_b1, RobustPoint const& robust_b2)
+ {
+ BOOST_CONCEPT_ASSERT( (concept::ConstSegment<Segment1>) );
+ BOOST_CONCEPT_ASSERT( (concept::ConstSegment<Segment2>) );
- if (! robustness_verify_r(a, b, r))
- {
- return Policy::disjoint();
- }
+ boost::ignore_unused_variable_warning(robust_policy);
- robustness_handle_meeting(a, b, sides, dx_a, dy_a, wx, wy, d, r);
+ typedef typename select_calculation_type
+ <Segment1, Segment2, CalculationType>::type coordinate_type;
- if (robustness_verify_disjoint_at_one_collinear(a, b, sides))
- {
- return Policy::disjoint();
- }
+ using geometry::detail::equals::equals_point_point;
+ bool const a_is_point = equals_point_point(robust_a1, robust_a2);
+ bool const b_is_point = equals_point_point(robust_b1, robust_b2);
- }
- }
+ typedef side::side_by_triangle<coordinate_type> side;
- if(collinear)
+ if(a_is_point && b_is_point)
{
- if (collinear_use_first)
- {
- return relate_collinear<0>(a, b);
- }
- else
- {
- // Y direction contains larger segments (maybe dx is zero)
- return relate_collinear<1>(a, b);
- }
+ return equals_point_point(robust_a1, robust_b2)
+ ? Policy::degenerate(a, true)
+ : Policy::disjoint()
+ ;
}
- return Policy::segments_intersect(sides, r,
- dx_a, dy_a, dx_b, dy_b,
- a, b);
- }
-
-private :
+ side_info sides;
+ sides.set<0>(side::apply(robust_b1, robust_b2, robust_a1),
+ side::apply(robust_b1, robust_b2, robust_a2));
+ sides.set<1>(side::apply(robust_a1, robust_a2, robust_b1),
+ side::apply(robust_a1, robust_a2, robust_b2));
+ bool collinear = sides.collinear();
- // Ratio should lie between 0 and 1
- // Also these three conditions might be of FP imprecision, the segments were actually (nearly) collinear
- template <typename T>
- static inline bool robustness_verify_r(
- segment_type1 const& a, segment_type2 const& b,
- T& r)
- {
- T const zero = 0;
- T const one = 1;
- if (r < zero || r > one)
+ if (sides.same<0>() || sides.same<1>())
{
- if (verify_disjoint<0>(a, b) || verify_disjoint<1>(a, b))
- {
- // Can still be disjoint (even if not one is left or right from another)
- // This is e.g. in case #snake4 of buffer test.
- return false;
- }
-
- //std::cout << "ROBUSTNESS: correction of r " << r << std::endl;
- // sides.debug();
+ // Both points are at same side of other segment, we can leave
+ return Policy::disjoint();
+ }
- // ROBUSTNESS: the r value can in epsilon-cases much larger than 1, while (with perfect arithmetic)
- // it should be one. It can be 1.14 or even 1.98049 or 2 (while still intersecting)
+ typedef typename select_most_precise
+ <
+ coordinate_type, double
+ >::type promoted_type;
- // If segments are crossing (we can see that with the sides)
- // and one is inside the other, there must be an intersection point.
- // We correct for that.
- // This is (only) in case #ggl_list_20110820_christophe in unit tests
+ typedef typename geometry::coordinate_type
+ <
+ RobustPoint
+ >::type robust_coordinate_type;
- // If segments are touching (two sides zero), of course they should intersect
- // This is (only) in case #buffer_rt_i in the unit tests)
+ typedef typename segment_ratio_type
+ <
+ typename geometry::point_type<Segment1>::type, // TODO: most precise point?
+ RobustPolicy
+ >::type ratio_type;
- // If one touches in the middle, they also should intersect (#buffer_rt_j)
+ segment_intersection_info
+ <
+ coordinate_type,
+ promoted_type,
+ ratio_type
+ > sinfo;
- // Note that even for ttmath r is occasionally > 1, e.g. 1.0000000000000000000000036191231203575
+ sinfo.dx_a = get<1, 0>(a) - get<0, 0>(a); // distance in x-dir
+ sinfo.dx_b = get<1, 0>(b) - get<0, 0>(b);
+ sinfo.dy_a = get<1, 1>(a) - get<0, 1>(a); // distance in y-dir
+ sinfo.dy_b = get<1, 1>(b) - get<0, 1>(b);
- if (r > one)
- {
- r = one;
- }
- else if (r < zero)
- {
- r = zero;
- }
- }
- return true;
- }
+ robust_coordinate_type const robust_dx_a = get<0>(robust_a2) - get<0>(robust_a1);
+ robust_coordinate_type const robust_dx_b = get<0>(robust_b2) - get<0>(robust_b1);
+ robust_coordinate_type const robust_dy_a = get<1>(robust_a2) - get<1>(robust_a1);
+ robust_coordinate_type const robust_dy_b = get<1>(robust_b2) - get<1>(robust_b1);
- static inline void robustness_verify_collinear(
- segment_type1 const& a, segment_type2 const& b,
- side_info& sides,
- bool& collinear)
- {
- if ((sides.zero<0>() && ! sides.zero<1>()) || (sides.zero<1>() && ! sides.zero<0>()))
+ // r: ratio 0-1 where intersection divides A/B
+ // (only calculated for non-collinear segments)
+ if (! collinear)
{
- // If one of the segments is collinear, the other must be as well.
- // So handle it as collinear.
- // (In float/double epsilon margins it can easily occur that one or two of them are -1/1)
- // sides.debug();
- sides.set<0>(0,0);
- sides.set<1>(0,0);
- collinear = true;
- }
- }
+ robust_coordinate_type robust_da0, robust_da;
+ robust_coordinate_type robust_db0, robust_db;
- static inline void robustness_verify_meeting(
- segment_type1 const& a, segment_type2 const& b,
- side_info& sides,
- bool& collinear, bool& collinear_use_first)
- {
- if (sides.meeting())
- {
- // If two segments meet each other at their segment-points, two sides are zero,
- // the other two are not (unless collinear but we don't mean those here).
- // However, in near-epsilon ranges it can happen that two sides are zero
- // but they do not meet at their segment-points.
- // In that case they are nearly collinear and handled as such.
- if (! point_equals
- (
- select(sides.zero_index<0>(), a),
- select(sides.zero_index<1>(), b)
- )
- )
+ cramers_rule(robust_dx_a, robust_dy_a, robust_dx_b, robust_dy_b,
+ get<0>(robust_a1) - get<0>(robust_b1),
+ get<1>(robust_a1) - get<1>(robust_b1),
+ robust_da0, robust_da);
+
+ cramers_rule(robust_dx_b, robust_dy_b, robust_dx_a, robust_dy_a,
+ get<0>(robust_b1) - get<0>(robust_a1),
+ get<1>(robust_b1) - get<1>(robust_a1),
+ robust_db0, robust_db);
+
+ if (robust_da0 == 0)
{
+ // If this is the case, no rescaling is done for FP precision.
+ // We set it to collinear, but it indicates a robustness issue.
sides.set<0>(0,0);
sides.set<1>(0,0);
collinear = true;
-
- if (collinear_use_first && analyse_equal<0>(a, b))
- {
- collinear_use_first = false;
- }
- else if (! collinear_use_first && analyse_equal<1>(a, b))
- {
- collinear_use_first = true;
- }
-
}
- }
- }
-
- // Verifies and if necessary correct missed touch because of robustness
- // This is the case at multi_polygon_buffer unittest #rt_m
- static inline bool robustness_verify_same_side(
- segment_type1 const& a, segment_type2 const& b,
- side_info& sides)
- {
- int corrected = 0;
- if (sides.one_touching<0>())
- {
- if (point_equals(
- select(sides.zero_index<0>(), a),
- select(0, b)
- ))
- {
- sides.correct_to_zero<1, 0>();
- corrected = 1;
- }
- if (point_equals
- (
- select(sides.zero_index<0>(), a),
- select(1, b)
- ))
+ else
{
- sides.correct_to_zero<1, 1>();
- corrected = 2;
+ sinfo.robust_ra.assign(robust_da, robust_da0);
+ sinfo.robust_rb.assign(robust_db, robust_db0);
}
}
- else if (sides.one_touching<1>())
+
+ if (collinear)
{
- if (point_equals(
- select(sides.zero_index<1>(), b),
- select(0, a)
- ))
+ bool const collinear_use_first
+ = geometry::math::abs(robust_dx_a) + geometry::math::abs(robust_dx_b)
+ >= geometry::math::abs(robust_dy_a) + geometry::math::abs(robust_dy_b);
+
+ // Degenerate cases: segments of single point, lying on other segment, are not disjoint
+ // This situation is collinear too
+
+ if (collinear_use_first)
{
- sides.correct_to_zero<0, 0>();
- corrected = 3;
+ return relate_collinear<0, ratio_type>(a, b,
+ robust_a1, robust_a2, robust_b1, robust_b2,
+ a_is_point, b_is_point);
}
- if (point_equals
- (
- select(sides.zero_index<1>(), b),
- select(1, a)
- ))
+ else
{
- sides.correct_to_zero<0, 1>();
- corrected = 4;
+ // Y direction contains larger segments (maybe dx is zero)
+ return relate_collinear<1, ratio_type>(a, b,
+ robust_a1, robust_a2, robust_b1, robust_b2,
+ a_is_point, b_is_point);
}
}
- return corrected == 0;
+ return Policy::segments_crosses(sides, sinfo, a, b);
}
- static inline bool robustness_verify_disjoint_at_one_collinear(
- segment_type1 const& a, segment_type2 const& b,
- side_info const& sides)
+private:
+ template
+ <
+ std::size_t Dimension,
+ typename RatioType,
+ typename Segment1,
+ typename Segment2,
+ typename RobustPoint
+ >
+ static inline return_type relate_collinear(Segment1 const& a,
+ Segment2 const& b,
+ RobustPoint const& robust_a1, RobustPoint const& robust_a2,
+ RobustPoint const& robust_b1, RobustPoint const& robust_b2,
+ bool a_is_point, bool b_is_point)
{
- if (sides.one_of_all_zero())
+ if (a_is_point)
{
- if (verify_disjoint<0>(a, b) || verify_disjoint<1>(a, b))
- {
- return true;
- }
+ return relate_one_degenerate<RatioType>(a,
+ get<Dimension>(robust_a1),
+ get<Dimension>(robust_b1), get<Dimension>(robust_b2),
+ true);
}
- return false;
- }
-
-
- // If r is one, or zero, segments should meet and their endpoints.
- // Robustness issue: check if this is really the case.
- // It turns out to be no problem, see buffer test #rt_s1 (and there are many cases generated)
- // It generates an "ends in the middle" situation which is correct.
- template <typename T, typename R>
- static inline void robustness_handle_meeting(segment_type1 const& a, segment_type2 const& b,
- side_info& sides,
- T const& dx_a, T const& dy_a, T const& wx, T const& wy,
- T const& d, R const& r)
- {
- return;
-
- T const db = geometry::detail::determinant<T>(dx_a, dy_a, wx, wy);
-
- R const zero = 0;
- R const one = 1;
- if (math::equals(r, zero) || math::equals(r, one))
+ if (b_is_point)
{
- R rb = db / d;
- if (rb <= 0 || rb >= 1 || math::equals(rb, 0) || math::equals(rb, 1))
- {
- if (sides.one_zero<0>() && ! sides.one_zero<1>()) // or vice versa
- {
-#if defined(BOOST_GEOMETRY_COUNT_INTERSECTION_EQUAL)
- extern int g_count_intersection_equal;
- g_count_intersection_equal++;
-#endif
- sides.debug();
- std::cout << "E r=" << r << " r.b=" << rb << " ";
- }
- }
+ return relate_one_degenerate<RatioType>(b,
+ get<Dimension>(robust_b1),
+ get<Dimension>(robust_a1), get<Dimension>(robust_a2),
+ false);
}
+ return relate_collinear<RatioType>(a, b,
+ get<Dimension>(robust_a1),
+ get<Dimension>(robust_a2),
+ get<Dimension>(robust_b1),
+ get<Dimension>(robust_b2));
}
- template <std::size_t Dimension>
- static inline bool verify_disjoint(segment_type1 const& a,
- segment_type2 const& b)
- {
- coordinate_type a_1, a_2, b_1, b_2;
- bool a_swapped = false, b_swapped = false;
- detail::segment_arrange<Dimension>(a, a_1, a_2, a_swapped);
- detail::segment_arrange<Dimension>(b, b_1, b_2, b_swapped);
- return math::smaller(a_2, b_1) || math::larger(a_1, b_2);
- }
-
- template <typename Segment>
- static inline typename point_type<Segment>::type select(int index, Segment const& segment)
- {
- return index == 0
- ? detail::get_from_index<0>(segment)
- : detail::get_from_index<1>(segment)
- ;
- }
-
- // We cannot use geometry::equals here. Besides that this will be changed
- // to compare segment-coordinate-values directly (not necessary to retrieve point first)
- template <typename Point1, typename Point2>
- static inline bool point_equals(Point1 const& point1, Point2 const& point2)
- {
- return math::equals(get<0>(point1), get<0>(point2))
- && math::equals(get<1>(point1), get<1>(point2))
- ;
- }
-
- // We cannot use geometry::equals here. Besides that this will be changed
- // to compare segment-coordinate-values directly (not necessary to retrieve point first)
- template <typename Point1, typename Point2>
- static inline bool point_equality(Point1 const& point1, Point2 const& point2,
- bool& equals_0, bool& equals_1)
- {
- equals_0 = math::equals(get<0>(point1), get<0>(point2));
- equals_1 = math::equals(get<1>(point1), get<1>(point2));
- return equals_0 && equals_1;
- }
-
- template <std::size_t Dimension>
- static inline bool analyse_equal(segment_type1 const& a, segment_type2 const& b)
- {
- coordinate_type const a_1 = geometry::get<0, Dimension>(a);
- coordinate_type const a_2 = geometry::get<1, Dimension>(a);
- coordinate_type const b_1 = geometry::get<0, Dimension>(b);
- coordinate_type const b_2 = geometry::get<1, Dimension>(b);
- return math::equals(a_1, b_1)
- || math::equals(a_2, b_1)
- || math::equals(a_1, b_2)
- || math::equals(a_2, b_2)
- ;
- }
-
- template <std::size_t Dimension>
- static inline return_type relate_collinear(segment_type1 const& a,
- segment_type2 const& b)
+ /// Relate segments known collinear
+ template
+ <
+ typename RatioType,
+ typename Segment1,
+ typename Segment2,
+ typename RobustType
+ >
+ static inline return_type relate_collinear(Segment1 const& a
+ , Segment2 const& b
+ , RobustType oa_1, RobustType oa_2
+ , RobustType ob_1, RobustType ob_2
+ )
{
- coordinate_type a_1, a_2, b_1, b_2;
- bool a_swapped = false, b_swapped = false;
- detail::segment_arrange<Dimension>(a, a_1, a_2, a_swapped);
- detail::segment_arrange<Dimension>(b, b_1, b_2, b_swapped);
- if (math::smaller(a_2, b_1) || math::larger(a_1, b_2))
- //if (a_2 < b_1 || a_1 > b_2)
+ // Calculate the ratios where a starts in b, b starts in a
+ // a1--------->a2 (2..7)
+ // b1----->b2 (5..8)
+ // length_a: 7-2=5
+ // length_b: 8-5=3
+ // b1 is located w.r.t. a at ratio: (5-2)/5=3/5 (on a)
+ // b2 is located w.r.t. a at ratio: (8-2)/5=6/5 (right of a)
+ // a1 is located w.r.t. b at ratio: (2-5)/3=-3/3 (left of b)
+ // a2 is located w.r.t. b at ratio: (7-5)/3=2/3 (on b)
+ // A arrives (a2 on b), B departs (b1 on a)
+
+ // If both are reversed:
+ // a2<---------a1 (7..2)
+ // b2<-----b1 (8..5)
+ // length_a: 2-7=-5
+ // length_b: 5-8=-3
+ // b1 is located w.r.t. a at ratio: (8-7)/-5=-1/5 (before a starts)
+ // b2 is located w.r.t. a at ratio: (5-7)/-5=2/5 (on a)
+ // a1 is located w.r.t. b at ratio: (7-8)/-3=1/3 (on b)
+ // a2 is located w.r.t. b at ratio: (2-8)/-3=6/3 (after b ends)
+
+ // If both one is reversed:
+ // a1--------->a2 (2..7)
+ // b2<-----b1 (8..5)
+ // length_a: 7-2=+5
+ // length_b: 5-8=-3
+ // b1 is located w.r.t. a at ratio: (8-2)/5=6/5 (after a ends)
+ // b2 is located w.r.t. a at ratio: (5-2)/5=3/5 (on a)
+ // a1 is located w.r.t. b at ratio: (2-8)/-3=6/3 (after b ends)
+ // a2 is located w.r.t. b at ratio: (7-8)/-3=1/3 (on b)
+ RobustType const length_a = oa_2 - oa_1; // no abs, see above
+ RobustType const length_b = ob_2 - ob_1;
+
+ RatioType const ra_from(oa_1 - ob_1, length_b);
+ RatioType const ra_to(oa_2 - ob_1, length_b);
+ RatioType const rb_from(ob_1 - oa_1, length_a);
+ RatioType const rb_to(ob_2 - oa_1, length_a);
+
+ if ((ra_from.left() && ra_to.left()) || (ra_from.right() && ra_to.right()))
{
return Policy::disjoint();
}
- return relate_collinear(a, b, a_1, a_2, b_1, b_2, a_swapped, b_swapped);
+
+ return Policy::segments_collinear(a, b, ra_from, ra_to, rb_from, rb_to);
}
- /// Relate segments known collinear
- static inline return_type relate_collinear(segment_type1 const& a
- , segment_type2 const& b
- , coordinate_type a_1, coordinate_type a_2
- , coordinate_type b_1, coordinate_type b_2
- , bool a_swapped, bool b_swapped)
+ /// Relate segments where one is degenerate
+ template
+ <
+ typename RatioType,
+ typename DegenerateSegment,
+ typename RobustType
+ >
+ static inline return_type relate_one_degenerate(
+ DegenerateSegment const& degenerate_segment
+ , RobustType d
+ , RobustType s1, RobustType s2
+ , bool a_degenerate
+ )
{
- // All ca. 150 lines are about collinear rays
- // The intersections, if any, are always boundary points of the segments. No need to calculate anything.
- // However we want to find out HOW they intersect, there are many cases.
- // Most sources only provide the intersection (above) or that there is a collinearity (but not the points)
- // or some spare sources give the intersection points (calculated) but not how they align.
- // This source tries to give everything and still be efficient.
- // It is therefore (and because of the extensive clarification comments) rather long...
-
- // \see http://mpa.itc.it/radim/g50history/CMP/4.2.1-CERL-beta-libes/file475.txt
- // \see http://docs.codehaus.org/display/GEOTDOC/Point+Set+Theory+and+the+DE-9IM+Matrix
- // \see http://mathworld.wolfram.com/Line-LineIntersection.html
-
- // Because of collinearity the case is now one-dimensional and can be checked using intervals
- // This function is called either horizontally or vertically
- // We get then two intervals:
- // a_1-------------a_2 where a_1 < a_2
- // b_1-------------b_2 where b_1 < b_2
- // In all figures below a_1/a_2 denotes arranged intervals, a1-a2 or a2-a1 are still unarranged
-
- // Handle "equal", in polygon neighbourhood comparisons a common case
-
- bool const opposite = a_swapped ^ b_swapped;
- bool const both_swapped = a_swapped && b_swapped;
-
- // Check if segments are equal or opposite equal...
- bool const swapped_a1_eq_b1 = math::equals(a_1, b_1);
- bool const swapped_a2_eq_b2 = math::equals(a_2, b_2);
-
- if (swapped_a1_eq_b1 && swapped_a2_eq_b2)
- {
- return Policy::segment_equal(a, opposite);
- }
-
- bool const swapped_a2_eq_b1 = math::equals(a_2, b_1);
- bool const swapped_a1_eq_b2 = math::equals(a_1, b_2);
-
- bool const a1_eq_b1 = both_swapped ? swapped_a2_eq_b2 : a_swapped ? swapped_a2_eq_b1 : b_swapped ? swapped_a1_eq_b2 : swapped_a1_eq_b1;
- bool const a2_eq_b2 = both_swapped ? swapped_a1_eq_b1 : a_swapped ? swapped_a1_eq_b2 : b_swapped ? swapped_a2_eq_b1 : swapped_a2_eq_b2;
-
- bool const a1_eq_b2 = both_swapped ? swapped_a2_eq_b1 : a_swapped ? swapped_a2_eq_b2 : b_swapped ? swapped_a1_eq_b1 : swapped_a1_eq_b2;
- bool const a2_eq_b1 = both_swapped ? swapped_a1_eq_b2 : a_swapped ? swapped_a1_eq_b1 : b_swapped ? swapped_a2_eq_b2 : swapped_a2_eq_b1;
-
-
-
-
- // The rest below will return one or two intersections.
- // The delegated class can decide which is the intersection point, or two, build the Intersection Matrix (IM)
- // For IM it is important to know which relates to which. So this information is given,
- // without performance penalties to intersection calculation
-
- bool const has_common_points = swapped_a1_eq_b1 || swapped_a1_eq_b2 || swapped_a2_eq_b1 || swapped_a2_eq_b2;
-
-
- // "Touch" -> one intersection point -> one but not two common points
- // --------> A (or B)
- // <---------- B (or A)
- // a_2==b_1 (b_2==a_1 or a_2==b1)
-
- // The check a_2/b_1 is necessary because it excludes cases like
- // ------->
- // --->
- // ... which are handled lateron
-
- // Corresponds to 4 cases, of which the equal points are determined above
- // #1: a1---->a2 b1--->b2 (a arrives at b's border)
- // #2: a2<----a1 b2<---b1 (b arrives at a's border)
- // #3: a1---->a2 b2<---b1 (both arrive at each others border)
- // #4: a2<----a1 b1--->b2 (no arrival at all)
- // Where the arranged forms have two forms:
- // a_1-----a_2/b_1-------b_2 or reverse (B left of A)
- if ((swapped_a2_eq_b1 || swapped_a1_eq_b2) && ! swapped_a1_eq_b1 && ! swapped_a2_eq_b2)
- {
- if (a2_eq_b1) return Policy::collinear_touch(get<1, 0>(a), get<1, 1>(a), 0, -1);
- if (a1_eq_b2) return Policy::collinear_touch(get<0, 0>(a), get<0, 1>(a), -1, 0);
- if (a2_eq_b2) return Policy::collinear_touch(get<1, 0>(a), get<1, 1>(a), 0, 0);
- if (a1_eq_b1) return Policy::collinear_touch(get<0, 0>(a), get<0, 1>(a), -1, -1);
- }
-
-
- // "Touch/within" -> there are common points and also an intersection of interiors:
- // Corresponds to many cases:
- // #1a: a1------->a2 #1b: a1-->a2
- // b1--->b2 b1------->b2
- // #2a: a2<-------a1 #2b: a2<--a1
- // b1--->b2 b1------->b2
- // #3a: a1------->a2 #3b: a1-->a2
- // b2<---b1 b2<-------b1
- // #4a: a2<-------a1 #4b: a2<--a1
- // b2<---b1 b2<-------b1
-
- // Note: next cases are similar and handled by the code
- // #4c: a1--->a2
- // b1-------->b2
- // #4d: a1-------->a2
- // b1-->b2
-
- // For case 1-4: a_1 < (b_1 or b_2) < a_2, two intersections are equal to segment B
- // For case 5-8: b_1 < (a_1 or a_2) < b_2, two intersections are equal to segment A
- if (has_common_points)
- {
- // Either A is in B, or B is in A, or (in case of robustness/equals)
- // both are true, see below
- bool a_in_b = (b_1 < a_1 && a_1 < b_2) || (b_1 < a_2 && a_2 < b_2);
- bool b_in_a = (a_1 < b_1 && b_1 < a_2) || (a_1 < b_2 && b_2 < a_2);
-
- if (a_in_b && b_in_a)
- {
- // testcase "ggl_list_20110306_javier"
- // In robustness it can occur that a point of A is inside B AND a point of B is inside A,
- // still while has_common_points is true (so one point equals the other).
- // If that is the case we select on length.
- coordinate_type const length_a = geometry::math::abs(a_1 - a_2);
- coordinate_type const length_b = geometry::math::abs(b_1 - b_2);
- if (length_a > length_b)
- {
- a_in_b = false;
- }
- else
- {
- b_in_a = false;
- }
- }
-
- int const arrival_a = a_in_b ? 1 : -1;
- if (a2_eq_b2) return Policy::collinear_interior_boundary_intersect(a_in_b ? a : b, a_in_b, 0, 0, false);
- if (a1_eq_b2) return Policy::collinear_interior_boundary_intersect(a_in_b ? a : b, a_in_b, arrival_a, 0, true);
- if (a2_eq_b1) return Policy::collinear_interior_boundary_intersect(a_in_b ? a : b, a_in_b, 0, -arrival_a, true);
- if (a1_eq_b1) return Policy::collinear_interior_boundary_intersect(a_in_b ? a : b, a_in_b, arrival_a, -arrival_a, false);
- }
-
-
-
- // "Inside", a completely within b or b completely within a
- // 2 cases:
- // case 1:
- // a_1---a_2 -> take A's points as intersection points
- // b_1------------b_2
- // case 2:
- // a_1------------a_2
- // b_1---b_2 -> take B's points
- if (a_1 > b_1 && a_2 < b_2)
- {
- // A within B
- return Policy::collinear_a_in_b(a, opposite);
- }
- if (b_1 > a_1 && b_2 < a_2)
- {
- // B within A
- return Policy::collinear_b_in_a(b, opposite);
- }
-
-
- /*
-
- Now that all cases with equal,touch,inside,disjoint,
- degenerate are handled the only thing left is an overlap
-
- Either a1 is between b1,b2
- or a2 is between b1,b2 (a2 arrives)
-
- Next table gives an overview.
- The IP's are ordered following the line A1->A2
-
- | |
- | a_2 in between | a_1 in between
- | |
- -----+---------------------------------+--------------------------
- | a1--------->a2 | a1--------->a2
- | b1----->b2 | b1----->b2
- | (b1,a2), a arrives | (a1,b2), b arrives
- | |
- -----+---------------------------------+--------------------------
- a sw.| a2<---------a1* | a2<---------a1*
- | b1----->b2 | b1----->b2
- | (a1,b1), no arrival | (b2,a2), a and b arrive
- | |
- -----+---------------------------------+--------------------------
- | a1--------->a2 | a1--------->a2
- b sw.| b2<-----b1 | b2<-----b1
- | (b2,a2), a and b arrive | (a1,b1), no arrival
- | |
- -----+---------------------------------+--------------------------
- a sw.| a2<---------a1* | a2<---------a1*
- b sw.| b2<-----b1 | b2<-----b1
- | (a1,b2), b arrives | (b1,a2), a arrives
- | |
- -----+---------------------------------+--------------------------
- * Note that a_1 < a_2, and a1 <> a_1; if a is swapped,
- the picture might seem wrong but it (supposed to be) is right.
- */
-
- if (b_1 < a_2 && a_2 < b_2)
- {
- // Left column, from bottom to top
- return
- both_swapped ? Policy::collinear_overlaps(get<0, 0>(a), get<0, 1>(a), get<1, 0>(b), get<1, 1>(b), -1, 1, opposite)
- : b_swapped ? Policy::collinear_overlaps(get<1, 0>(b), get<1, 1>(b), get<1, 0>(a), get<1, 1>(a), 1, 1, opposite)
- : a_swapped ? Policy::collinear_overlaps(get<0, 0>(a), get<0, 1>(a), get<0, 0>(b), get<0, 1>(b), -1, -1, opposite)
- : Policy::collinear_overlaps(get<0, 0>(b), get<0, 1>(b), get<1, 0>(a), get<1, 1>(a), 1, -1, opposite)
- ;
- }
- if (b_1 < a_1 && a_1 < b_2)
- {
- // Right column, from bottom to top
- return
- both_swapped ? Policy::collinear_overlaps(get<0, 0>(b), get<0, 1>(b), get<1, 0>(a), get<1, 1>(a), 1, -1, opposite)
- : b_swapped ? Policy::collinear_overlaps(get<0, 0>(a), get<0, 1>(a), get<0, 0>(b), get<0, 1>(b), -1, -1, opposite)
- : a_swapped ? Policy::collinear_overlaps(get<1, 0>(b), get<1, 1>(b), get<1, 0>(a), get<1, 1>(a), 1, 1, opposite)
- : Policy::collinear_overlaps(get<0, 0>(a), get<0, 1>(a), get<1, 0>(b), get<1, 1>(b), -1, 1, opposite)
- ;
- }
- // Nothing should goes through. If any we have made an error
- // std::cout << "Robustness issue, non-logical behaviour" << std::endl;
- return Policy::error("Robustness issue, non-logical behaviour");
+ // Calculate the ratios where ds starts in s
+ // a1--------->a2 (2..6)
+ // b1/b2 (4..4)
+ // Ratio: (4-2)/(6-2)
+ RatioType const ratio(d - s1, s2 - s1);
+ return Policy::one_degenerate(degenerate_segment, ratio, a_degenerate);
}
};
}} // namespace strategy::intersection
-
-
}} // namespace boost::geometry