#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
-#include <boost/geometry/core/assert.hpp>
-#include <boost/geometry/arithmetic/infinite_line_functions.hpp>
#include <boost/geometry/algorithms/detail/make/make.hpp>
-
-#include <boost/core/ignore_unused.hpp>
-
+#include <boost/geometry/arithmetic/infinite_line_functions.hpp>
+#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry
{
namespace detail { namespace buffer
{
-// TODO: this might once be changed to a proper strategy
struct line_line_intersection
{
template <typename Point>
- static inline bool
- apply(Point const& pi, Point const& pj, Point const& qi, Point const& qj,
- Point& ip)
+ static Point between_point(Point const& a, Point const& b)
{
- typedef typename coordinate_type<Point>::type ct;
- typedef model::infinite_line<ct> line_type;
-
- line_type const p = detail::make::make_infinite_line<ct>(pi, pj);
- line_type const q = detail::make::make_infinite_line<ct>(qi, qj);
+ Point result;
+ geometry::set<0>(result, (geometry::get<0>(a) + geometry::get<0>(b)) / 2.0);
+ geometry::set<1>(result, (geometry::get<1>(a) + geometry::get<1>(b)) / 2.0);
+ return result;
+ }
- // The input lines are not parallel, they intersect, because
- // their join type is checked before.
- return arithmetic::intersection_point(p, q, ip);
+ template <typename Point>
+ static bool
+ apply(Point const& pi, Point const& pj, Point const& qi, Point const& qj,
+ Point const& vertex, bool equidistant, Point& ip)
+ {
+ // Calculates ip (below) by either intersecting p (pi, pj)
+ // with q (qi, qj) or by taking a point between pj and qi (b) and
+ // intersecting r (b, v), where v is the original vertex, with p (or q).
+ // The reason for dual approach: p might be nearly collinear with q,
+ // and in that case the intersection points can lose precision
+ // (or be plainly wrong).
+ // Therefore it takes the most precise option (this is usually p, r)
+ //
+ // /qj |
+ // / |
+ // / / |
+ // / / |
+ // / / |
+ // /qi / |
+ // / |
+ // ip * + b * v |
+ // \ |
+ // \pj \ |
+ // \ \ |
+ // \ \ |
+ // \ \ |
+ // \pi \ |
+ //
+ // If generated sides along the segments can have an adapted distance,
+ // in a custom strategy, then the calculation of the point in between
+ // might be incorrect and the optimization is not used.
+
+ using ct = typename coordinate_type<Point>::type;
+
+ auto const p = detail::make::make_infinite_line<ct>(pi, pj);
+ auto const q = detail::make::make_infinite_line<ct>(qi, qj);
+
+ using line = decltype(p);
+ using arithmetic::determinant;
+ using arithmetic::assign_intersection_point;
+
+ // The denominator is the determinant of (a,b) values of lines p q
+ // | pa pa |
+ // | qb qb |
+ auto const denominator_pq = determinant<line, &line::a, &line::b>(p, q);
+ constexpr decltype(denominator_pq) const zero = 0;
+
+ if (equidistant)
+ {
+ auto const between = between_point(pj, qi);
+ auto const r = detail::make::make_infinite_line<ct>(vertex, between);
+ auto const denominator_pr = determinant<line, &line::a, &line::b>(p, r);
+
+ if (math::equals(denominator_pq, zero)
+ && math::equals(denominator_pr, zero))
+ {
+ // Degenerate case (for example when length results in <inf>)
+ return false;
+ }
+
+ ip = geometry::math::abs(denominator_pq) > geometry::math::abs(denominator_pr)
+ ? assign_intersection_point<Point>(p, q, denominator_pq)
+ : assign_intersection_point<Point>(p, r, denominator_pr);
+ }
+ else
+ {
+ if (math::equals(denominator_pq, zero))
+ {
+ return false;
+ }
+ ip = assign_intersection_point<Point>(p, q, denominator_pq);
+ }
+
+ return true;
}
};