496 lines
20 KiB
C++
496 lines
20 KiB
C++
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
|
|
|
// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands.
|
|
|
|
// Use, modification and distribution is subject to the Boost Software License,
|
|
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_INTERSECTION_HPP
|
|
#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_INTERSECTION_HPP
|
|
|
|
#include <algorithm>
|
|
|
|
#include <boost/geometry/core/exception.hpp>
|
|
|
|
#include <boost/geometry/geometries/concepts/point_concept.hpp>
|
|
#include <boost/geometry/geometries/concepts/segment_concept.hpp>
|
|
|
|
#include <boost/geometry/algorithms/detail/assign_values.hpp>
|
|
|
|
#include <boost/geometry/util/math.hpp>
|
|
#include <boost/geometry/util/select_calculation_type.hpp>
|
|
|
|
// Temporary / will be Strategy as template parameter
|
|
#include <boost/geometry/strategies/side.hpp>
|
|
#include <boost/geometry/strategies/cartesian/side_by_triangle.hpp>
|
|
|
|
#include <boost/geometry/strategies/side_info.hpp>
|
|
|
|
|
|
namespace boost { namespace geometry
|
|
{
|
|
|
|
|
|
namespace strategy { namespace intersection
|
|
{
|
|
|
|
|
|
#ifndef DOXYGEN_NO_DETAIL
|
|
namespace detail
|
|
{
|
|
|
|
template <typename Segment, size_t Dimension>
|
|
struct segment_arrange
|
|
{
|
|
template <typename T>
|
|
static inline void apply(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)
|
|
{
|
|
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
|
|
*/
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
// 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)
|
|
{
|
|
// 1) Handle "disjoint", probably common case.
|
|
// per dimension, 2 cases: a_1----------a_2 b_1-------b_2 or B left of A
|
|
coordinate_type ax_1, ax_2, bx_1, bx_2;
|
|
bool ax_swapped = false, bx_swapped = false;
|
|
detail::segment_arrange<segment_type1, 0>::apply(a, ax_1, ax_2, ax_swapped);
|
|
detail::segment_arrange<segment_type2, 0>::apply(b, bx_1, bx_2, bx_swapped);
|
|
if (ax_2 < bx_1 || ax_1 > bx_2)
|
|
{
|
|
return Policy::disjoint();
|
|
}
|
|
|
|
// 1b) In Y-dimension
|
|
coordinate_type ay_1, ay_2, by_1, by_2;
|
|
bool ay_swapped = false, by_swapped = false;
|
|
detail::segment_arrange<segment_type1, 1>::apply(a, ay_1, ay_2, ay_swapped);
|
|
detail::segment_arrange<segment_type2, 1>::apply(b, by_1, by_2, by_swapped);
|
|
if (ay_2 < ay_1 || ay_1 > by_2)
|
|
{
|
|
return Policy::disjoint();
|
|
}
|
|
|
|
typedef side::side_by_triangle<coordinate_type> side;
|
|
side_info sides;
|
|
|
|
// 2) Calculate sides
|
|
// Note: Do NOT yet calculate the determinant here, but use the SIDE strategy.
|
|
// Determinant calculation is not robust; side (orient) can be made robust
|
|
// (and is much robuster even without measures)
|
|
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))
|
|
);
|
|
|
|
if (sides.same<1>())
|
|
{
|
|
// Both points are at same side of other segment, we can leave
|
|
return Policy::disjoint();
|
|
}
|
|
|
|
// 2b) For other segment
|
|
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))
|
|
);
|
|
|
|
if (sides.same<0>())
|
|
{
|
|
return Policy::disjoint();
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
bool collinear = sides.collinear();
|
|
|
|
// Get the same type, but at least a double (also used for divisions)
|
|
typedef typename select_most_precise
|
|
<
|
|
coordinate_type, double
|
|
>::type promoted_type;
|
|
|
|
|
|
promoted_type const d = (dy_b * dx_a) - (dx_b * dy_a);
|
|
// Determinant d should be nonzero.
|
|
// If it is zero, we have an robustness issue here,
|
|
// (and besides that we cannot divide by it)
|
|
if(math::equals(d, zero) && ! collinear)
|
|
//if(! collinear && sides.as_collinear())
|
|
{
|
|
#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION
|
|
std::cout << "Determinant zero? Type : "
|
|
<< typeid(coordinate_type).name()
|
|
<< std::endl;
|
|
|
|
std::cout << " dx_a : " << dx_a << std::endl;
|
|
std::cout << " dy_a : " << dy_a << std::endl;
|
|
std::cout << " dx_b : " << dx_b << std::endl;
|
|
std::cout << " dy_b : " << dy_b << std::endl;
|
|
|
|
std::cout << " side a <-> b.first : " << sides.get<0,0>() << std::endl;
|
|
std::cout << " side a <-> b.second: " << sides.get<0,1>() << std::endl;
|
|
std::cout << " side b <-> a.first : " << sides.get<1,0>() << std::endl;
|
|
std::cout << " side b <-> a.second: " << sides.get<1,1>() << std::endl;
|
|
#endif
|
|
|
|
if (sides.as_collinear())
|
|
{
|
|
sides.set<0>(0,0);
|
|
sides.set<1>(0,0);
|
|
collinear = true;
|
|
}
|
|
else
|
|
{
|
|
return Policy::error("Determinant zero!");
|
|
}
|
|
}
|
|
|
|
if(collinear)
|
|
{
|
|
// Segments are collinear. We'll find out how.
|
|
if (math::equals(dx_b, zero))
|
|
{
|
|
// Vertical -> Check y-direction
|
|
return relate_collinear(a, b,
|
|
ay_1, ay_2, by_1, by_2,
|
|
ay_swapped, by_swapped);
|
|
}
|
|
else
|
|
{
|
|
// Check x-direction
|
|
return relate_collinear(a, b,
|
|
ax_1, ax_2, bx_1, bx_2,
|
|
ax_swapped, bx_swapped);
|
|
}
|
|
}
|
|
|
|
return Policy::segments_intersect(sides,
|
|
dx_a, dy_a, dx_b, dy_b,
|
|
a, b);
|
|
}
|
|
|
|
private :
|
|
|
|
/// 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)
|
|
{
|
|
// 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
|
|
|
|
// Check if segments are equal...
|
|
bool const a1_eq_b1 = math::equals(get<0, 0>(a), get<0, 0>(b))
|
|
&& math::equals(get<0, 1>(a), get<0, 1>(b));
|
|
bool const a2_eq_b2 = math::equals(get<1, 0>(a), get<1, 0>(b))
|
|
&& math::equals(get<1, 1>(a), get<1, 1>(b));
|
|
if (a1_eq_b1 && a2_eq_b2)
|
|
{
|
|
return Policy::segment_equal(a, false);
|
|
}
|
|
|
|
// ... or opposite equal
|
|
bool const a1_eq_b2 = math::equals(get<0, 0>(a), get<1, 0>(b))
|
|
&& math::equals(get<0, 1>(a), get<1, 1>(b));
|
|
bool const a2_eq_b1 = math::equals(get<1, 0>(a), get<0, 0>(b))
|
|
&& math::equals(get<1, 1>(a), get<0, 1>(b));
|
|
if (a1_eq_b2 && a2_eq_b1)
|
|
{
|
|
return Policy::segment_equal(a, true);
|
|
}
|
|
|
|
|
|
// 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 = a1_eq_b1 || a1_eq_b2 || a2_eq_b1 || 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 (has_common_points && (math::equals(a_2, b_1) || math::equals(b_2, a_1)))
|
|
{
|
|
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);
|
|
}
|
|
|
|
bool const opposite = a_swapped ^ b_swapped;
|
|
|
|
|
|
// "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.
|
|
*/
|
|
|
|
bool const both_swapped = a_swapped && b_swapped;
|
|
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
|
|
// Robustness: it can occur here...
|
|
return Policy::error("Robustness issue, non-logical behaviour");
|
|
}
|
|
};
|
|
|
|
|
|
}} // namespace strategy::intersection
|
|
|
|
|
|
|
|
}} // namespace boost::geometry
|
|
|
|
|
|
#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_INTERSECTION_HPP
|