Merge branch 'develop' of https://github.com/boostorg/geometry into feature/is_simple

This commit is contained in:
Menelaos Karavelas
2014-06-25 01:54:15 +03:00
6 changed files with 156 additions and 70 deletions

View File

@@ -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);
}

View File

@@ -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)
{

View File

@@ -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;
}
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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);