[buffer] add join_selector and use it before generating the join.

This moves responsibility of generation to the calling code, removes it
from the join strategies. It also enables using the end-strategy for the
spikes
This commit is contained in:
Barend Gehrels 2014-06-23 22:39:29 +02:00
parent d8a4d12c8e
commit aa307b5ad2
8 changed files with 92 additions and 85 deletions

View File

@ -22,6 +22,7 @@
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#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>
@ -44,7 +45,6 @@ struct buffer_range
typedef typename point_type<RingOutput>::type output_point_type;
typedef typename coordinate_type<RingOutput>::type coordinate_type;
template
<
typename Point,
@ -90,6 +90,7 @@ struct buffer_range
typename RobustPolicy
>
static inline void add_join(Collection& collection,
strategy::buffer::join_selector join,
Point const& previous_input,
output_point_type const& prev_perp1,
output_point_type const& prev_perp2,
@ -101,26 +102,67 @@ struct buffer_range
JoinStrategy const& join_strategy,
RobustPolicy const& )
{
output_point_type intersection_point;
if (line_line_intersection::apply(
perp1, perp2, prev_perp1, prev_perp2, intersection_point))
switch(join)
{
std::vector<output_point_type> range_out;
if (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);
}
else
{
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;
}
output_point_type intersection_point;
line_line_intersection::apply(
perp1, perp2, prev_perp1, prev_perp2,
intersection_point);
std::vector<output_point_type> range_out;
if (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);
}
static inline strategy::buffer::join_selector get_join_type(
output_point_type const& p0,
output_point_type const& p1,
output_point_type const& p2)
{
typedef typename strategy::side::services::default_strategy
<
typename cs_tag<output_point_type>::type
>::type side_strategy;
int const side = side_strategy::apply(p0, p1, p2);
return side == -1 ? strategy::buffer::join_convex
: side == 1 ? strategy::buffer::join_concave
: parallel_continue
(
get<0>(p2) - get<0>(p1),
get<1>(p2) - get<1>(p1),
get<0>(p1) - get<0>(p0),
get<1>(p1) - get<1>(p0)
) ? strategy::buffer::join_continue
: strategy::buffer::join_spike;
}
template
@ -155,7 +197,7 @@ struct buffer_range
robust_point_type previous_robust_input;
output_point_type previous_p1, previous_p2;
output_point_type first_p1, first_p2;
point_type penultimate_point, ultimate_point; // last two points from begin/end
point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end
bool first = true;
@ -175,6 +217,7 @@ struct buffer_range
if (! first)
{
add_join(collection,
get_join_type(penultimate_point, *prev, *it),
*prev, previous_p1, previous_p2,
*it, p1, p2,
side,
@ -191,6 +234,7 @@ struct buffer_range
if (first)
{
first = false;
second_point = *it;
first_p1 = p1;
first_p2 = p2;
}
@ -205,6 +249,7 @@ struct buffer_range
{
// Generate closing corner
add_join(collection,
get_join_type(penultimate_point, ultimate_point, second_point),
*(end - 1), previous_p1, previous_p2,
*begin, first_p1, first_p2,
side,

View File

@ -32,15 +32,6 @@ struct line_line_intersection
return a * d - b * c;
}
// Returns true if two parallel segments (vectors dx1,dy1 and dx2,dy2)
// move forward (one continues each other - they have the same signs)
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);
}
template <typename Point>
static inline bool apply(Point const& pi, Point const& pj,
Point const& qi, Point const& qj, Point& ip)
@ -50,7 +41,7 @@ struct line_line_intersection
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: maybe use something else then denominator (sides?) to determine this.
// TODO: code below will be removed - curve type is now checked before
// 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.
@ -59,28 +50,9 @@ struct line_line_intersection
coordinate_type const limit = 1.0e-9;
if (geometry::math::abs(denominator) < limit)
{
// If denominator is small or zero, segments are (nearly) parallel
// Either they continue each other
// +---------------+--------------+
// x1,y1 x2,y2=x3,y3 x4,y4
// We then return false
// Or they form a spikey feature
// x1,y1
// +---------------+ x2,y2
// +---------------/ x3,y4
// x4,y4
// We then calculate the IP from one of the segments up to a certain distance
if (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)))
{
return false;
}
// Spikey, set something arbitrary and calculate px,py far away
denominator = limit;
}
// 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));

View File

@ -70,6 +70,19 @@ enum piece_type
};
/*!
\brief Enumerates types of joins
\ingroup enum
*/
enum join_selector
{
join_convex,
join_concave,
join_continue, // collinear, next segment touches previous segment
join_spike // collinear, with overlap, next segment goes back
};
}} // namespace strategy::buffer

View File

@ -11,7 +11,6 @@
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/strategies/tags.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
@ -33,7 +32,6 @@ template
>
struct join_miter
{
typedef typename strategy::side::services::default_strategy<typename cs_tag<PointIn>::type>::type side;
typedef typename coordinate_type<PointIn>::type coordinate_type;
// Constructor compatible with other join strategies:
@ -46,16 +44,6 @@ struct join_miter
coordinate_type const& buffer_distance,
RangeOut& range_out) const
{
coordinate_type const zero = 0;
int const signum = buffer_distance > zero ? 1
: buffer_distance < zero ? -1
: 0;
if (side::apply(perp1, ip, perp2) == signum)
{
return false;
}
PointIn p = ip;
// Normalize it and give it X*dist.

View File

@ -13,7 +13,6 @@
#include <boost/geometry/policies/compare.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/strategies/tags.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
@ -42,7 +41,6 @@ public :
: m_steps_per_circle(steps_per_circle)
{}
typedef typename strategy::side::services::default_strategy<typename cs_tag<PointIn>::type>::type side;
typedef typename coordinate_type<PointOut>::type coordinate_type;
typedef typename geometry::select_most_precise
@ -121,16 +119,6 @@ public :
return false;
}
coordinate_type const zero = 0;
int const signum = buffer_distance > zero ? 1
: buffer_distance < zero ? -1
: 0;
if (side::apply(perp1, ip, perp2) == signum)
{
return false;
}
// Generate 'vectors'
coordinate_type vix = (get<0>(ip) - get<0>(vertex));
coordinate_type viy = (get<1>(ip) - get<1>(vertex));

View File

@ -13,7 +13,6 @@
#include <boost/geometry/policies/compare.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/strategies/tags.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
@ -42,7 +41,6 @@ public :
: m_max_level(max_level)
{}
typedef typename strategy::side::services::default_strategy<typename cs_tag<PointIn>::type>::type side;
typedef typename coordinate_type<PointOut>::type coordinate_type;
typedef typename geometry::select_most_precise
@ -110,16 +108,6 @@ public :
return false;
}
coordinate_type const zero = 0;
int const signum = buffer_distance > zero ? 1
: buffer_distance < zero ? -1
: 0;
if (side::apply(perp1, ip, perp2) == signum)
{
return false;
}
// Generate 'vectors'
coordinate_type vix = (get<0>(ip) - get<0>(vertex));
coordinate_type viy = (get<1>(ip) - get<1>(vertex));

View File

@ -28,6 +28,7 @@ static std::string const one_bend = "LINESTRING(0 0,4 5,7 4)";
static std::string const two_bends = "LINESTRING(0 0,4 5,7 4,10 6)";
static std::string const overlapping = "LINESTRING(0 0,4 5,7 4,10 6, 10 2,2 2)";
static std::string const curve = "LINESTRING(2 7,3 5,5 4,7 5,8 7)";
static std::string const tripod = "LINESTRING(5 0,5 5,1 8,5 5,9 8)"; // with spike
static std::string const for_collinear = "LINESTRING(2 0,0 0,0 4,6 4,6 0,4 0)";
static std::string const for_collinear2 = "LINESTRING(2 1,2 0,0 0,0 4,6 4,6 0,4 0,4 1)";
@ -116,6 +117,9 @@ 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_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);
test_one<linestring, buf::join_round, buf::end_flat, polygon>("chained4", chained4, 22.6274, 2.5, 1.5);

View File

@ -24,6 +24,9 @@ static std::string const spike_simplex
static std::string const chained_box
= "POLYGON((0 0,0 4,4 4,8 4,12 4,12 0,8 0,4 0,0 0))";
static std::string const join_types
= "POLYGON ((0 0,0 4,4 4,2 6,0 8,2 6,4 8,8 4,4 0,0 0))"; // first 4 join types are all different: convex, concave, continue, spike
static std::string const donut_simplex
= "POLYGON ((0 0,1 9,8 1,0 0),(1 1,4 1,1 4,1 1))";
static std::string const donut_diamond
@ -99,6 +102,8 @@ void test_all()
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_flat, polygon_type>("join_types", join_types, 91.7379, 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);
test_one<polygon_type, buf::join_round, buf::end_skip, polygon_type>("L", letter_L, 13.7314, 0.5);
@ -275,12 +280,16 @@ 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);