[buffer] fix for integer overflow

This commit is contained in:
Barend Gehrels 2022-06-29 14:49:06 +02:00
parent 756412ae0f
commit 1837ddc7f1
8 changed files with 164 additions and 32 deletions

View File

@ -24,8 +24,6 @@
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
namespace boost { namespace geometry
{

View File

@ -89,8 +89,8 @@ public:
// Check the distance ip-vertex (= miter distance)
// (We calculate it manually (not using Pythagoras strategy) to reuse
// dx and dy)
coordinate_type const dx = get<0>(p) - get<0>(vertex);
coordinate_type const dy = get<1>(p) - get<1>(vertex);
promoted_type const dx = get<0>(p) - get<0>(vertex);
promoted_type const dy = get<1>(p) - get<1>(vertex);
promoted_type const distance = geometry::math::sqrt(dx * dx + dy * dy);

View File

@ -149,20 +149,8 @@ public :
return false;
}
// Generate 'vectors'
coordinate_type vix = (get<0>(ip) - get<0>(vertex));
coordinate_type viy = (get<1>(ip) - get<1>(vertex));
promoted_type length_i = geometry::math::sqrt(vix * vix + viy * viy);
DistanceType const bd = geometry::math::abs(buffer_distance);
promoted_type prop = bd / length_i;
Point bp;
set<0>(bp, get<0>(vertex) + vix * prop);
set<1>(bp, get<1>(vertex) + viy * prop);
range_out.push_back(perp1);
generate_points<promoted_type>(vertex, perp1, perp2, bd, range_out);
generate_points<promoted_type>(vertex, perp1, perp2, geometry::math::abs(buffer_distance), range_out);
range_out.push_back(perp2);
return true;
}

View File

@ -48,23 +48,21 @@ public :
RangeOut& range_out,
std::size_t level = 1) const
{
typedef typename coordinate_type<Point>::type coordinate_type;
// Generate 'vectors'
coordinate_type const vp1_x = get<0>(p1) - get<0>(vertex);
coordinate_type const vp1_y = get<1>(p1) - get<1>(vertex);
PromotedType const vp1_x = get<0>(p1) - get<0>(vertex);
PromotedType const vp1_y = get<1>(p1) - get<1>(vertex);
coordinate_type const vp2_x = (get<0>(p2) - get<0>(vertex));
coordinate_type const vp2_y = (get<1>(p2) - get<1>(vertex));
PromotedType const vp2_x = (get<0>(p2) - get<0>(vertex));
PromotedType const vp2_y = (get<1>(p2) - get<1>(vertex));
// Average them to generate vector in between
coordinate_type const two = 2;
coordinate_type const v_x = (vp1_x + vp2_x) / two;
coordinate_type const v_y = (vp1_y + vp2_y) / two;
PromotedType const two = 2;
PromotedType const v_x = (vp1_x + vp2_x) / two;
PromotedType const v_y = (vp1_y + vp2_y) / two;
PromotedType const length2 = geometry::math::sqrt(v_x * v_x + v_y * v_y);
PromotedType prop = buffer_distance / length2;
PromotedType const prop = buffer_distance / length2;
Point mid_point;
set<0>(mid_point, get<0>(vertex) + v_x * prop);
@ -106,13 +104,13 @@ public :
}
// Generate 'vectors'
coordinate_type const vix = (get<0>(ip) - get<0>(vertex));
coordinate_type const viy = (get<1>(ip) - get<1>(vertex));
promoted_type const vix = (get<0>(ip) - get<0>(vertex));
promoted_type const viy = (get<1>(ip) - get<1>(vertex));
promoted_type const length_i = geometry::math::sqrt(vix * vix + viy * viy);
promoted_type const bd = geometry::math::abs(buffer_distance);
promoted_type prop = bd / length_i;
promoted_type const prop = bd / length_i;
Point bp;
set<0>(bp, get<0>(vertex) + vix * prop);

View File

@ -76,8 +76,8 @@ public :
// Generate a block along (left or right of) the segment
// Simulate a vector d (dx,dy)
coordinate_type const dx = get<0>(input_p2) - get<0>(input_p1);
coordinate_type const dy = get<1>(input_p2) - get<1>(input_p1);
promoted_type const dx = get<0>(input_p2) - get<0>(input_p1);
promoted_type const dy = get<1>(input_p2) - get<1>(input_p1);
// For normalization [0,1] (=dot product d.d, sqrt)
promoted_type const length = geometry::math::sqrt(dx * dx + dy * dy);

View File

@ -18,6 +18,8 @@
test-suite boost-geometry-strategies
:
[ run andoyer.cpp : : : : strategies_andoyer ]
[ run buffer_join.cpp : : : : strategies_buffer_join ]
[ run buffer_side_straight.cpp : : : : strategies_buffer_side_straight ]
[ run cross_track.cpp : : : : strategies_cross_track ]
[ run crossings_multiply.cpp : : : : strategies_crossings_multiply ]
[ run distance_default_result.cpp : : : : strategies_distance_default_result ]

View File

@ -0,0 +1,87 @@
// Boost.Geometry
// Unit Test
// Copyright (c) 2022 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)
#include <geometry_test_common.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/ring.hpp>
#include <boost/geometry/strategies/cartesian/buffer_join_round.hpp>
#include <boost/geometry/strategies/cartesian/buffer_join_round_by_divide.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/distance.hpp>
#include <boost/geometry/algorithms/equals.hpp>
template <typename JoinStrategy, typename P, typename T>
void test_join(JoinStrategy const& join,
P const& ip, P const& vertex,
P const& perp1, P const& perp2,
T const& buffer_distance, std::size_t expected_size)
{
// Use a deque to be able to use push_front
bg::model::ring<P, true, true, std::deque> output_ring;
auto const result = join.apply(ip, vertex, perp1, perp2, buffer_distance, output_ring);
BOOST_CHECK_EQUAL(true, result);
BOOST_CHECK_EQUAL(expected_size , output_ring.size());
BOOST_CHECK(bg::equals(perp1, output_ring.front()));
BOOST_CHECK(bg::equals(perp2, output_ring.back()));
// All the generated points should be located
// at or close to the specified buffer distance from the vertex
for (const auto& p : output_ring)
{
auto const d = bg::distance(vertex, p);
auto const fraction = d / buffer_distance;
BOOST_CHECK_MESSAGE(fraction > 0.92 && fraction < 1.02,
"Unexpected distance = " << d << " fraction=" << fraction);
}
// Start with the vertex itself, and close it,
// to calculate the area of the buffered corner
output_ring.push_front(vertex);
output_ring.push_back(vertex);
// Area should be around 0.25 * PI * sqr(specified buffer distance)
// It is a bit less because there are less than infinite points in the circle
double const area = bg::area(output_ring) ;
double const expected = 0.25 * buffer_distance * buffer_distance * 3.1415;
double const fraction_area = area / expected;
BOOST_CHECK_MESSAGE(fraction_area > 0.95 && fraction_area < 1.05,
"Unexpected area = " << area << " fraction=" << fraction_area);
}
template <typename P>
void test_case(typename bg::coordinate_type<P>::type c)
{
// For round strategy (36 points per circle) we expect 10 points in a corner.
// Using divide on level 3, we get 17
test_join(bg::strategy::buffer::join_round(36), P{c, c}, P{0, 0}, P{0, c}, P{c, 0}, double(c), 10);
test_join(bg::strategy::buffer::join_round_by_divide(3), P{c, c}, P{0, 0}, P{0, c}, P{c, 0}, double(c), 17);
}
template <typename P>
void test_all()
{
// Also test for large coordinates.
// For integer coordinates there was an overflow with them (now fixed)
test_case<P>(100);
test_case<P>(10000);
test_case<P>(1000000);
test_case<P>(100000000);
}
int test_main(int, char *[])
{
test_all<bg::model::point<int, 2, bg::cs::cartesian>>();
test_all<bg::model::point<float, 2, bg::cs::cartesian>>();
test_all<bg::model::point<double, 2, bg::cs::cartesian>>();
return 0;
}

View File

@ -0,0 +1,59 @@
// Boost.Geometry
// Unit Test
// Copyright (c) 2022 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)
#include <geometry_test_common.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/strategies/cartesian/buffer_side_straight.hpp>
#include <boost/geometry/strategies/agnostic/buffer_distance_symmetric.hpp>
#include <boost/geometry/algorithms/distance.hpp>
template <typename P>
void test_side_straight(P const &input_p1, P const &input_p2)
{
using bg::strategy::buffer::side_straight;
bg::strategy::buffer::distance_symmetric<typename bg::coordinate_type<P>::type> ds(1.0);
std::vector<P> output_range;
auto const result = side_straight::apply(input_p1, input_p2, bg::strategy::buffer::buffer_side_right, ds, output_range);
BOOST_CHECK_EQUAL(bg::strategy::buffer::result_normal, result);
BOOST_CHECK_EQUAL(2u, output_range.size());
#ifdef BOOST_GEOMETRY_TEST_DEBUG
if (output_range.size() == 2)
{
std::cout << bg::distance(output_range.front(), output_range.back()) << std::endl;
}
#endif
}
template <typename P>
void test_all()
{
// Also test for large coordinates.
// For integer coordinates there was an overflow with them (now fixed)
test_side_straight(P{0, 0}, P{1, 1});
test_side_straight(P{0, 0}, P{100, 100});
test_side_straight(P{0, 0}, P{10000, 10000});
test_side_straight(P{0, 0}, P{1000000, 1000000});
test_side_straight(P{0, 0}, P{100000000, 100000000});
}
int test_main(int, char *[])
{
test_all<bg::model::point<int, 2, bg::cs::cartesian>>();
test_all<bg::model::point<double, 2, bg::cs::cartesian>>();
return 0;
}