mirror of
https://github.com/boostorg/geometry.git
synced 2026-02-13 00:22:10 +00:00
Merge branch 'develop' of https://github.com/boostorg/geometry into feature/is_simple
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include <boost/geometry/strategies/side.hpp>
|
||||
#include <boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp>
|
||||
#include <boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp>
|
||||
#include <boost/geometry/algorithms/detail/buffer/parallel_continue.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
@@ -87,10 +88,13 @@ struct buffer_range
|
||||
typename Point,
|
||||
typename DistanceStrategy,
|
||||
typename JoinStrategy,
|
||||
typename EndStrategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void add_join(Collection& collection,
|
||||
strategy::buffer::join_selector join,
|
||||
static inline
|
||||
void add_join(Collection& collection,
|
||||
int phase,
|
||||
Point const& penultimate_input,
|
||||
Point const& previous_input,
|
||||
output_point_type const& prev_perp1,
|
||||
output_point_type const& prev_perp2,
|
||||
@@ -100,46 +104,55 @@ struct buffer_range
|
||||
strategy::buffer::buffer_side_selector side,
|
||||
DistanceStrategy const& distance,
|
||||
JoinStrategy const& join_strategy,
|
||||
EndStrategy const& end_strategy,
|
||||
RobustPolicy const& )
|
||||
{
|
||||
output_point_type intersection_point;
|
||||
|
||||
strategy::buffer::join_selector join
|
||||
= get_join_type(penultimate_input, previous_input, input);
|
||||
if (join == strategy::buffer::join_convex)
|
||||
{
|
||||
// Calculate the intersection-point formed by the two sides.
|
||||
// It might be that the two sides are not convex, but continue
|
||||
// or spikey, we then change the join-type
|
||||
join = line_line_intersection::apply(
|
||||
perp1, perp2, prev_perp1, prev_perp2,
|
||||
intersection_point);
|
||||
|
||||
}
|
||||
switch(join)
|
||||
{
|
||||
case strategy::buffer::join_continue :
|
||||
// No join, we get two consecutive sides
|
||||
return;
|
||||
case strategy::buffer::join_convex :
|
||||
break; // All code below handles this
|
||||
case strategy::buffer::join_concave :
|
||||
collection.add_piece(strategy::buffer::buffered_concave,
|
||||
previous_input, prev_perp2, perp1);
|
||||
return;
|
||||
case strategy::buffer::join_spike :
|
||||
// TODO use end strategy
|
||||
// For now: use join strategy
|
||||
//return;
|
||||
break;
|
||||
//if (phase == 0) avoid duplicate joins at spikes? this still causes other issues
|
||||
{
|
||||
// For linestrings, only add spike at one side to avoid
|
||||
// duplicates
|
||||
std::vector<output_point_type> range_out;
|
||||
end_strategy.apply(penultimate_input, prev_perp2, previous_input, perp1, side, distance, range_out);
|
||||
collection.add_endcap(end_strategy, range_out, previous_input);
|
||||
}
|
||||
return;
|
||||
case strategy::buffer::join_convex :
|
||||
break; // All code below handles this
|
||||
}
|
||||
|
||||
output_point_type intersection_point;
|
||||
line_line_intersection::apply(
|
||||
perp1, perp2, prev_perp1, prev_perp2,
|
||||
intersection_point);
|
||||
// The corner is convex, we create a join
|
||||
// TODO - try to avoid a separate vector, add the piece directly
|
||||
std::vector<output_point_type> range_out;
|
||||
if (join_strategy.apply(intersection_point,
|
||||
join_strategy.apply(intersection_point,
|
||||
previous_input, prev_perp2, perp1,
|
||||
distance.apply(previous_input, input, side),
|
||||
range_out))
|
||||
{
|
||||
collection.add_piece(strategy::buffer::buffered_join,
|
||||
previous_input, range_out);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2)
|
||||
{
|
||||
return math::sign(dx1) == math::sign(dx2)
|
||||
&& math::sign(dy1) == math::sign(dy2);
|
||||
range_out);
|
||||
collection.add_piece(strategy::buffer::buffered_join,
|
||||
previous_input, range_out);
|
||||
}
|
||||
|
||||
static inline strategy::buffer::join_selector get_join_type(
|
||||
@@ -175,6 +188,7 @@ struct buffer_range
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void iterate(Collection& collection,
|
||||
int phase, // 0/1 for left/right of rings. For polygons: 0
|
||||
Iterator begin, Iterator end,
|
||||
strategy::buffer::buffer_side_selector side,
|
||||
DistanceStrategy const& distance_strategy,
|
||||
@@ -199,6 +213,23 @@ struct buffer_range
|
||||
output_point_type first_p1, first_p2;
|
||||
point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end
|
||||
|
||||
/*
|
||||
* prev.p1 prev.p2 these are the "previous perpendicular points"
|
||||
* --------------
|
||||
* | |
|
||||
* *------------*____ <- *prev
|
||||
* pup | | p1 "current perpendiculat point 1"
|
||||
* | |
|
||||
* | | this forms a "side", a side is a piece
|
||||
* | |
|
||||
* *____| p2
|
||||
*
|
||||
* ^
|
||||
* *it
|
||||
*
|
||||
* pup: penultimate_point
|
||||
*/
|
||||
|
||||
bool first = true;
|
||||
|
||||
Iterator it = begin;
|
||||
@@ -216,12 +247,13 @@ struct buffer_range
|
||||
|
||||
if (! first)
|
||||
{
|
||||
add_join(collection,
|
||||
get_join_type(penultimate_point, *prev, *it),
|
||||
*prev, previous_p1, previous_p2,
|
||||
*it, p1, p2,
|
||||
side,
|
||||
distance_strategy, join_strategy, robust_policy);
|
||||
add_join(collection, phase,
|
||||
penultimate_point,
|
||||
*prev, previous_p1, previous_p2,
|
||||
*it, p1, p2,
|
||||
side,
|
||||
distance_strategy, join_strategy, end_strategy,
|
||||
robust_policy);
|
||||
}
|
||||
collection.add_piece(strategy::buffer::buffered_segment,
|
||||
*prev, *it, p1, p2, first);
|
||||
@@ -248,12 +280,13 @@ struct buffer_range
|
||||
if(boost::is_same<Tag, ring_tag>::value)
|
||||
{
|
||||
// Generate closing corner
|
||||
add_join(collection,
|
||||
get_join_type(penultimate_point, ultimate_point, second_point),
|
||||
add_join(collection, phase,
|
||||
penultimate_point,
|
||||
*(end - 1), previous_p1, previous_p2,
|
||||
*begin, first_p1, first_p2,
|
||||
*(begin + 1), first_p1, first_p2,
|
||||
side,
|
||||
distance_strategy, join_strategy, robust_policy);
|
||||
distance_strategy, join_strategy, end_strategy,
|
||||
robust_policy);
|
||||
|
||||
// Buffer is closed automatically by last closing corner (NOT FOR OPEN POLYGONS - TODO)
|
||||
}
|
||||
@@ -271,7 +304,6 @@ struct buffer_range
|
||||
distance_strategy, rp2, rp1);
|
||||
|
||||
std::vector<output_point_type> range_out;
|
||||
|
||||
end_strategy.apply(penultimate_point, previous_p2, ultimate_point, rp2, side, distance_strategy, range_out);
|
||||
collection.add_endcap(end_strategy, range_out, ultimate_point);
|
||||
}
|
||||
@@ -485,13 +517,13 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput>
|
||||
// Walk backwards (rings will be reversed afterwards)
|
||||
// It might be that this will be changed later.
|
||||
// TODO: decide this.
|
||||
base::iterate(collection, boost::rbegin(ring), boost::rend(ring),
|
||||
base::iterate(collection, 0, boost::rbegin(ring), boost::rend(ring),
|
||||
strategy::buffer::buffer_side_right,
|
||||
distance, join_strategy, end_strategy, robust_policy);
|
||||
}
|
||||
else
|
||||
{
|
||||
base::iterate(collection, boost::begin(ring), boost::end(ring),
|
||||
base::iterate(collection, 0, boost::begin(ring), boost::end(ring),
|
||||
strategy::buffer::buffer_side_left,
|
||||
distance, join_strategy, end_strategy, robust_policy);
|
||||
}
|
||||
@@ -535,11 +567,11 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon>
|
||||
if (boost::size(linestring) > 1)
|
||||
{
|
||||
collection.start_new_ring();
|
||||
base::iterate(collection, boost::begin(linestring), boost::end(linestring),
|
||||
base::iterate(collection, 0, boost::begin(linestring), boost::end(linestring),
|
||||
strategy::buffer::buffer_side_left,
|
||||
distance, join_strategy, end_strategy, robust_policy);
|
||||
|
||||
base::iterate(collection, boost::rbegin(linestring), boost::rend(linestring),
|
||||
base::iterate(collection, 1, boost::rbegin(linestring), boost::rend(linestring),
|
||||
strategy::buffer::buffer_side_right,
|
||||
distance, join_strategy, end_strategy, robust_policy, true);
|
||||
}
|
||||
|
||||
@@ -667,6 +667,10 @@ struct buffered_piece_collection
|
||||
template <typename EndcapStrategy, typename Range>
|
||||
inline void add_endcap(EndcapStrategy const& strategy, Range const& range, point_type const& end_point)
|
||||
{
|
||||
if (range.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
strategy::buffer::piece_type pt = strategy.get_piece_type();
|
||||
if (pt == strategy::buffer::buffered_flat_end)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/arithmetic/determinant.hpp>
|
||||
#include <boost/geometry/util/math.hpp>
|
||||
#include <boost/geometry/strategies/buffer.hpp>
|
||||
#include <boost/geometry/algorithms/detail/buffer/parallel_continue.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
@@ -24,43 +27,54 @@ namespace detail { namespace buffer
|
||||
// TODO: once change this to proper strategy
|
||||
// It is different from current segment intersection because these are not segments but lines
|
||||
// If we have the Line concept, we can create a strategy
|
||||
// Assumes a convex corner
|
||||
struct line_line_intersection
|
||||
{
|
||||
template <typename A, typename B, typename C, typename D>
|
||||
static inline A det(A const& a, B const& b, C const& c, D const& d)
|
||||
{
|
||||
return a * d - b * c;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
static inline bool apply(Point const& pi, Point const& pj,
|
||||
static inline strategy::buffer::join_selector apply(Point const& pi, Point const& pj,
|
||||
Point const& qi, Point const& qj, Point& ip)
|
||||
{
|
||||
// See http://mathworld.wolfram.com/Line-LineIntersection.html
|
||||
typedef typename coordinate_type<Point>::type coordinate_type;
|
||||
|
||||
coordinate_type denominator = det(get<0>(pi) - get<0>(pj), get<1>(pi) - get<1>(pj), get<0>(qi) - get<0>(qj), get<1>(qi) - get<1>(qj));
|
||||
|
||||
// TODO: code below will be removed - curve type is now checked before
|
||||
coordinate_type const denominator
|
||||
= determinant<coordinate_type>(get<0>(pi) - get<0>(pj),
|
||||
get<1>(pi) - get<1>(pj),
|
||||
get<0>(qi) - get<0>(qj),
|
||||
get<1>(qi) - get<1>(qj));
|
||||
|
||||
// The limit is arbitrary. If it is small, the IP will be far far away.
|
||||
// For round joins, it will not be used at all.
|
||||
// For miter joints, there is a miter limit
|
||||
// If segments are parallel we must be distinguish two cases
|
||||
|
||||
// Even if the corner was checked before (so it is convex now), that
|
||||
// was done on the original geometry. This function runs on the buffered
|
||||
// geometries, where sides are generated and might be slightly off. In
|
||||
// Floating Point, that slightly might just exceed the limit and we have
|
||||
// to check it again.
|
||||
coordinate_type const limit = 1.0e-9;
|
||||
if (geometry::math::abs(denominator) < limit)
|
||||
{
|
||||
denominator = limit;
|
||||
return parallel_continue(get<0>(qj) - get<0>(qi),
|
||||
- get<1>(qj) - get<1>(qi),
|
||||
- get<0>(pj) - get<0>(pi),
|
||||
- get<1>(pj) - get<1>(pi))
|
||||
? strategy::buffer::join_continue
|
||||
: strategy::buffer::join_spike
|
||||
;
|
||||
}
|
||||
// END TODO
|
||||
|
||||
coordinate_type d1 = det(get<0>(pi), get<1>(pi), get<0>(pj), get<1>(pj));
|
||||
coordinate_type d2 = det(get<0>(qi), get<1>(qi), get<0>(qj), get<1>(qj));
|
||||
coordinate_type d1 = determinant<coordinate_type>(get<0>(pi), get<1>(pi), get<0>(pj), get<1>(pj));
|
||||
coordinate_type d2 = determinant<coordinate_type>(get<0>(qi), get<1>(qi), get<0>(qj), get<1>(qj));
|
||||
|
||||
set<0>(ip, det(d1, get<0>(pi) - get<0>(pj), d2, get<0>(qi) - get<0>(qj)) / denominator);
|
||||
set<1>(ip, det(d1, get<1>(pi) - get<1>(pj), d2, get<1>(qi) - get<1>(qj)) / denominator);
|
||||
double const multiplier = 1.0 / denominator;
|
||||
|
||||
return true;
|
||||
set<0>(ip, determinant<coordinate_type>(d1, get<0>(pi) - get<0>(pj), d2, get<0>(qi) - get<0>(qj)) * multiplier);
|
||||
set<1>(ip, determinant<coordinate_type>(d1, get<1>(pi) - get<1>(pj), d2, get<1>(qi) - get<1>(qj)) * multiplier);
|
||||
|
||||
return strategy::buffer::join_convex;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2012-2014 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_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP
|
||||
|
||||
#include <boost/geometry/util/math.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace buffer
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2)
|
||||
{
|
||||
return math::sign(dx1) == math::sign(dx2)
|
||||
&& math::sign(dy1) == math::sign(dy2);
|
||||
}
|
||||
|
||||
}} // namespace detail::buffer
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP
|
||||
@@ -114,8 +114,8 @@ void test_all()
|
||||
test_one<linestring, buf::join_miter, buf::end_flat, polygon>("curve", curve, 55.3875, 5.0, 3.0);
|
||||
#endif
|
||||
|
||||
test_one<linestring, buf::join_miter, buf::end_flat, polygon>("tripod", tripod, 89.55, 3.0);
|
||||
test_one<linestring, buf::join_miter, buf::end_round, polygon>("tripod", tripod, 117.806, 3.0);
|
||||
test_one<linestring, buf::join_miter, buf::end_flat, polygon>("tripod", tripod, 74.25, 3.0);
|
||||
test_one<linestring, buf::join_miter, buf::end_round, polygon>("tripod", tripod, 116.6336, 3.0);
|
||||
|
||||
test_one<linestring, buf::join_round, buf::end_flat, polygon>("chained2", chained2, 11.3137, 2.5, 1.5);
|
||||
test_one<linestring, buf::join_round, buf::end_flat, polygon>("chained3", chained3, 16.9706, 2.5, 1.5);
|
||||
|
||||
@@ -90,14 +90,19 @@ void test_all()
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("concave_simplex", concave_simplex, 14.5616, 0.5);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("concave_simplex", concave_simplex, 16.3861, 0.5);
|
||||
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("spike_simplex15", spike_simplex, 50.3633, 1.5);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("spike_simplex15", spike_simplex, 55.3759, 1.5);
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("spike_simplex30", spike_simplex, 100.9199, 3.0);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("spike_simplex30", spike_simplex, 120.9859, 3.0);
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("spike_simplex150", spike_simplex, 998.9530, 15.0);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("spike_simplex150", spike_simplex, 1532.6543, 15.0);
|
||||
test_one<polygon_type, buf::join_round, buf::end_round, polygon_type>("spike_simplex15", spike_simplex, 50.3633, 1.5);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_flat, polygon_type>("spike_simplex15", spike_simplex, 51.5509, 1.5);
|
||||
|
||||
test_one<polygon_type, buf::join_round, buf::end_flat, polygon_type>("join_types", join_types, 91.7379, 1.5);
|
||||
#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS)
|
||||
test_one<polygon_type, buf::join_round, buf::end_round, polygon_type>("spike_simplex30", spike_simplex, 100.9199, 3.0);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_flat, polygon_type>("spike_simplex30", spike_simplex, 120.9859, 3.0);
|
||||
#endif
|
||||
test_one<polygon_type, buf::join_round, buf::end_round, polygon_type>("spike_simplex150", spike_simplex, 998.9530, 15.0);
|
||||
#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS)
|
||||
test_one<polygon_type, buf::join_miter, buf::end_flat, polygon_type>("spike_simplex150", spike_simplex, 1532.6543, 15.0);
|
||||
#endif
|
||||
|
||||
test_one<polygon_type, buf::join_round, buf::end_flat, polygon_type>("join_types", join_types, 88.2060, 1.5);
|
||||
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("chained_box", chained_box, 83.1403, 1.0);
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("chained_box", chained_box, 84, 1.0);
|
||||
@@ -275,16 +280,12 @@ void test_all()
|
||||
|
||||
#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS)
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("parcel3_10", parcel3, 99, 10.0);
|
||||
#endif
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("parcel3_10", parcel3, 20022.4271087646484, 10.0);
|
||||
#endif
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("parcel3_20", parcel3, 34504.8032569885254, 20.0, -999, false);
|
||||
#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS)
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("parcel3_20", parcel3, 34615.6553726196289, 20.0);
|
||||
#endif
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("parcel3_30", parcel3, 45263.0166702270508, 30.0, -999, false);
|
||||
#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS)
|
||||
test_one<polygon_type, buf::join_miter, buf::end_skip, polygon_type>("parcel3_30", parcel3, 45506.1910133361816, 30.0);
|
||||
#endif
|
||||
|
||||
// Negative buffers making polygons smaller
|
||||
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("simplex", simplex, 7.04043, -0.5);
|
||||
|
||||
Reference in New Issue
Block a user