From 70ecdfdd19c8390be963ffbce5a6fd535cf031f2 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 May 2020 13:04:08 +0200 Subject: [PATCH] [buffer] Fix direction code for some new cases. The similar_direction function could be erroneous for corner cases, refactored it out. This also makes it a bit less dependent on infinite_line (cartesian) --- .../detail/buffer/buffer_inserter.hpp | 45 +++----- .../detail/buffer/line_line_intersection.hpp | 32 +++--- .../algorithms/detail/direction_code.hpp | 32 ++++-- .../arithmetic/infinite_line_functions.hpp | 13 --- .../buffer/buffer_multi_polygon.cpp | 18 +++ test/algorithms/buffer/test_buffer_svg.hpp | 3 +- test/arithmetic/infinite_line_functions.cpp | 71 ------------ test/strategies/Jamfile | 1 - test/strategies/side_of_intersection.cpp | 108 ------------------ 9 files changed, 70 insertions(+), 253 deletions(-) delete mode 100644 test/strategies/side_of_intersection.cpp diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 9d1faee77..976f011e2 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2020 Barend Gehrels, Amsterdam, the Netherlands. // This file was modified by Oracle on 2017. // Modifications copyright (c) 2017 Oracle and/or its affiliates. @@ -31,16 +31,13 @@ #include #include -#include +#include #include #include -#include #include #include -#include - #include @@ -113,23 +110,11 @@ struct buffer_range JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& , - SideStrategy const& side_strategy) // side strategy + SideStrategy const& side_strategy) { - output_point_type intersection_point; - geometry::assign_zero(intersection_point); - - geometry::strategy::buffer::join_selector join - = get_join_type(penultimate_input, previous_input, input, side_strategy); - if (join == geometry::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); - - } + geometry::strategy::buffer::join_selector const join + = get_join_type(penultimate_input, previous_input, input, + side_strategy); switch(join) { @@ -163,6 +148,11 @@ struct buffer_range { // The corner is convex, we create a join // TODO (future) - avoid a separate vector, add the piece directly + output_point_type const + intersection_point + = line_line_intersection::apply(perp1, perp2, + prev_perp1, prev_perp2); + std::vector range_out; if (join_strategy.apply(intersection_point, previous_input, prev_perp2, perp1, @@ -177,14 +167,14 @@ struct buffer_range } } - static inline bool similar_direction(output_point_type const& p0, + // Returns true if collinear point p2 continues after p0 and p1. + // If it turns back (spike), it returns false. + static inline bool same_direction(output_point_type const& p0, output_point_type const& p1, output_point_type const& p2) { - typedef model::infinite_line line_type; - line_type const p = detail::make::make_infinite_line(p0, p1); - line_type const q = detail::make::make_infinite_line(p1, p2); - return arithmetic::similar_direction(p, q); + typedef typename cs_tag::type cs_tag; + return direction_code(p0, p1, p2) == 1; } template @@ -197,8 +187,7 @@ struct buffer_range int const side = side_strategy.apply(p0, p1, p2); return side == -1 ? geometry::strategy::buffer::join_convex : side == 1 ? geometry::strategy::buffer::join_concave - : similar_direction(p0, p1, p2) - ? geometry::strategy::buffer::join_continue + : same_direction(p0, p1, p2) ? geometry::strategy::buffer::join_continue : geometry::strategy::buffer::join_spike; } diff --git a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp index ec50d91a8..45adb5abd 100644 --- a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012-2019 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2020 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 @@ -9,10 +9,12 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP - +#include #include #include -#include + +#include + namespace boost { namespace geometry { @@ -26,10 +28,8 @@ namespace detail { namespace buffer struct line_line_intersection { template - static inline strategy::buffer::join_selector - apply(Point const& pi, Point const& pj, - Point const& qi, Point const& qj, - Point& ip) + static inline Point + apply(Point const& pi, Point const& pj, Point const& qi, Point const& qj) { typedef typename coordinate_type::type ct; typedef model::infinite_line line_type; @@ -37,18 +37,14 @@ struct line_line_intersection line_type const p = detail::make::make_infinite_line(pi, pj); line_type const q = detail::make::make_infinite_line(qi, qj); - if (arithmetic::intersection_point(p, q, ip)) - { - return strategy::buffer::join_convex; - } + // The input lines are not parallel, they intersect, because + // their join type is checked before. + Point ip; + bool const intersecting = arithmetic::intersection_point(p, q, ip); + BOOST_GEOMETRY_ASSERT(intersecting); + boost::ignore_unused(intersecting); - // The lines do not intersect. - // Distinguish between continuing lines (having a similar direction) - // and spikes (having the opposite direction). - return arithmetic::similar_direction(p, q) - ? strategy::buffer::join_continue - : strategy::buffer::join_spike - ; + return ip; } }; diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 38c65afa2..0e81a202a 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2015-2020 Barend Gehrels, Amsterdam, the Netherlands. // This file was modified by Oracle on 2015, 2017, 2019. // Modifications copyright (c) 2015-2019 Oracle and/or its affiliates. @@ -54,21 +54,29 @@ struct direction_code_impl typedef model::infinite_line line_type; - // point b is often equal to the specified point, check that first - line_type const q = detail::make::make_infinite_line(segment_b, point); - if (arithmetic::is_degenerate(q)) + // Situation and construction of perpendicular line + // + // P1 a--------------->b P2 + // | + // | + // v + // + // P1 is located right of the (directional) perpendicular line + // and therefore gets a negative side_value, and returns -1. + // P2 is to the left of the perpendicular line and returns 1. + // If the specified point is located on top of b, it returns 0. + + line_type const line + = detail::make::make_perpendicular_line(segment_a, + segment_b, segment_b); + + if (arithmetic::is_degenerate(line)) { return 0; } - line_type const p = detail::make::make_infinite_line(segment_a, segment_b); - if (arithmetic::is_degenerate(p)) - { - return 0; - } - - // p extends a-b if direction is similar - return arithmetic::similar_direction(p, q) ? 1 : -1; + calc_t const sv = arithmetic::side_value(line, point); + return sv == 0 ? 0 : sv > 0 ? 1 : -1; } }; diff --git a/include/boost/geometry/arithmetic/infinite_line_functions.hpp b/include/boost/geometry/arithmetic/infinite_line_functions.hpp index 0e8281483..a4496b9b0 100644 --- a/include/boost/geometry/arithmetic/infinite_line_functions.hpp +++ b/include/boost/geometry/arithmetic/infinite_line_functions.hpp @@ -76,19 +76,6 @@ side_value(model::infinite_line const& line, Point const& p) return side_value(line, geometry::get<0>(p), geometry::get<1>(p)); } -// Returns true for two lines which are supposed to be (close to) collinear -// (which is not checked) and have a similar direction -// (in practice up to 45 degrees, TO BE VERIFIED) -// true: -----------------> p -----------------> q -// false: -----------------> p <----------------- q -template -inline -bool similar_direction(const model::infinite_line& p, - const model::infinite_line& q) -{ - return p.a * q.a >= 0 && p.b * q.b >= 0; -} - template inline bool is_degenerate(const model::infinite_line& line) { diff --git a/test/algorithms/buffer/buffer_multi_polygon.cpp b/test/algorithms/buffer/buffer_multi_polygon.cpp index cd9ee11d6..db0780610 100644 --- a/test/algorithms/buffer/buffer_multi_polygon.cpp +++ b/test/algorithms/buffer/buffer_multi_polygon.cpp @@ -281,6 +281,19 @@ static std::string const rt_u12 static std::string const rt_u13 = "MULTIPOLYGON(((6 4,6 5,7 5,6 4)),((3 2,3 3,4 3,3 2)),((7 8,7 9,8 9,8 8,7 8)),((4 9,4 10,5 10,4 9)),((7 7,7 8,8 7,7 7)),((2 6,2 7,3 7,2 6)),((0 1,1 2,1 1,0 1)),((3 1,4 2,4 1,3 1)),((2 5,2 6,3 6,2 5)),((3 5,4 4,3 4,2 4,3 5)),((4 1,5 2,5 1,4 1)),((2 0,2 1,3 1,2 0)),((5 7,5 8,6 7,5 7)),((0 2,0 3,1 3,0 2)),((9 8,9 9,10 9,10 8,9 8)),((7 5,7 6,8 5,7 5)),((5 6,5 7,6 6,5 6)),((0 6,0 7,1 7,1 6,0 6)),((5 0,5 1,6 1,5 0)),((8 7,8 8,9 8,8 7)),((4.5 4.5,5 4,4 4,4 5,5 5,4.5 4.5)),((6 2,5 2,5 3,6 3,7 3,8 2,7 2,6 2)),((8 6,8 7,9 7,9 6,9 5,8 5,8 6)),((8 1,9 0,8 0,7 0,8 1)))"; +static std::string const rt_v1 + = "MULTIPOLYGON(((2 8,2 9,3 9,2 8)),((3 6,3 7,4 7,3 6)),((9 9,9 10,10 10,10 9,9 9)),((0 7,1 8,1 7,0 7)))"; + +static std::string const rt_v2 + = "MULTIPOLYGON(((8 4,8 5,9 5,9 4,8 4)),((2 5,2 6,3 5,2 5)),((9 7,9 8,10 8,10 7,9 7)),((2 2,2 3,3 2,2 2)),((6 6,7 5,6 5,6 6)),((6 6,6 7,7 7,7 6,6 6)),((8 9,9 9,8 8,8 9)),((8 9,7 9,8 10,8 9)))"; + +static std::string const rt_v3 + = "MULTIPOLYGON(((7 0,7 1,8 1,8 0,7 0)),((6 2,6 3,7 2,6 2)),((9 3,8 3,8 4,9 3)),((9 3,9 4,10 3,9 3)))"; + +static std::string const rt_v4 + = "MULTIPOLYGON(((5 4,5 5,6 5,6 4,5 4)),((7 1,6 1,7 2,7 1)),((7 1,8 1,8 0,7 0,7 1)),((6 1,5 1,5 2,6 1)))"; + + static std::string const neighbouring = "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((10 10,10 20,20 20,20 10,10 10)))"; @@ -502,6 +515,11 @@ void test_all() test_one("rt_u12", rt_u12, join_miter, end_flat, 142.1348, 1.0); test_one("rt_u13", rt_u13, join_miter, end_flat, 115.4853, 1.0); + test_one("rt_v1", rt_v1, join_round32, end_flat, 26.9994, 1.0); + test_one("rt_v2", rt_v2, join_round32, end_flat, 47.3510, 1.0); + test_one("rt_v3", rt_v3, join_round32, end_flat, 22.9158, 1.0); + test_one("rt_v4", rt_v4, join_round32, end_flat, 23.4146, 1.0); + test_one("neighbouring_small", neighbouring, join_round32, end_round32, 128.0, -1.0); diff --git a/test/algorithms/buffer/test_buffer_svg.hpp b/test/algorithms/buffer/test_buffer_svg.hpp index b4d20df56..87739e68e 100644 --- a/test/algorithms/buffer/test_buffer_svg.hpp +++ b/test/algorithms/buffer/test_buffer_svg.hpp @@ -157,9 +157,8 @@ private : << "/" << it->operations[1].enriched.get_next_turn_index() //<< " frac " << it->operations[0].fraction - // If you want to see (robust)point coordinates (e.g. to find duplicates) + // If you want to see point coordinates (e.g. to find duplicates) << std::endl << std::setprecision(16) << bg::wkt(it->point) - << std::endl << bg::wkt(it->robust_point) << std::endl; out << " " << bg::method_char(it->method) diff --git a/test/arithmetic/infinite_line_functions.cpp b/test/arithmetic/infinite_line_functions.cpp index 9c879178c..845513415 100755 --- a/test/arithmetic/infinite_line_functions.cpp +++ b/test/arithmetic/infinite_line_functions.cpp @@ -96,77 +96,6 @@ void test_get_intersection() verify_point_on_line(q, bg::get<0>(ip), bg::get<1>(ip)); } -template -void test_same_direction() -{ - bg::model::infinite_line p, q; - - // Exactly opposite, diagonal - p = bg::detail::make::make_infinite_line(2, 1, 12, 11); - q = bg::detail::make::make_infinite_line(12, 11, 2, 1); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Exactly opposite, horizontal - p = bg::detail::make::make_infinite_line(0, 0, 10, 0); - q = bg::detail::make::make_infinite_line(10, 0, 0, 0); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Exactly opposite, vertical - p = bg::detail::make::make_infinite_line(0, 0, 0, 10); - q = bg::detail::make::make_infinite_line(0, 10, 0, 0); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Exactly equal, diagonal - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(0, 0, 10, 10); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // Exactly equal, horizontal - p = bg::detail::make::make_infinite_line(0, 0, 10, 0); - q = bg::detail::make::make_infinite_line(0, 0, 10, 0); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // Exactly equal, vertical - p = bg::detail::make::make_infinite_line(0, 0, 0, 10); - q = bg::detail::make::make_infinite_line(0, 0, 0, 10); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // Coming together, diagonal - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(20, 20, 10, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Leaving from common point, diagonal - p = bg::detail::make::make_infinite_line(10, 10, 0, 0); - q = bg::detail::make::make_infinite_line(0, 0, 10, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Continuing each other, diagonal - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(10, 10, 20, 20); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // (Nearly) perpendicular - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(0, 0, -10, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // 45 deg - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(0, 0, 0, 10); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // a bit more than 45 deg - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(0, 0, -1, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // 135 deg - p = bg::detail::make::make_infinite_line(0, 0, 10, 10); - q = bg::detail::make::make_infinite_line(0, 0, -10, 0); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); -} - template void test_degenerate() { diff --git a/test/strategies/Jamfile b/test/strategies/Jamfile index d47835650..e8b8031cd 100644 --- a/test/strategies/Jamfile +++ b/test/strategies/Jamfile @@ -35,7 +35,6 @@ test-suite boost-geometry-strategies [ run segment_intersection_collinear.cpp : : : : strategies_segment_intersection_collinear ] [ run segment_intersection_geo.cpp : : : : strategies_segment_intersection_geo ] [ run segment_intersection_sph.cpp : : : : strategies_segment_intersection_sph ] -# [ run side_of_intersection.cpp : : : : strategies_side_of_intersection ] [ run spherical_side.cpp : : : : strategies_spherical_side ] [ run thomas.cpp : : : : strategies_thomas ] [ run transform_cs.cpp : : : : strategies_transform_cs ] diff --git a/test/strategies/side_of_intersection.cpp b/test/strategies/side_of_intersection.cpp deleted file mode 100644 index b42270e3d..000000000 --- a/test/strategies/side_of_intersection.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) -// Unit Test - -// Copyright (c) 2011-2015 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 - -#include -#include -#include - - -namespace bg = boost::geometry; - -int test_main(int, char* []) -{ - typedef bg::model::d2::point_xy point; - typedef bg::model::segment segment; - - typedef bg::strategy::side::side_of_intersection side; - - point no_fb(-99, -99); - - segment a(point(20, 10), point(10, 20)); - - segment b1(point(11, 16), point(20, 14)); // IP with a: (14.857, 15.143) - segment b2(point(10, 16), point(20, 14)); // IP with a: (15, 15) - - segment c1(point(15, 16), point(13, 8)); - segment c2(point(15, 16), point(14, 8)); - segment c3(point(15, 16), point(15, 8)); - - - BOOST_CHECK_EQUAL( 1, side::apply(a, b1, c1, no_fb)); - BOOST_CHECK_EQUAL(-1, side::apply(a, b1, c2, no_fb)); - BOOST_CHECK_EQUAL(-1, side::apply(a, b1, c3, no_fb)); - - BOOST_CHECK_EQUAL( 1, side::apply(a, b2, c1, no_fb)); - BOOST_CHECK_EQUAL( 1, side::apply(a, b2, c2, no_fb)); - BOOST_CHECK_EQUAL( 0, side::apply(a, b2, c3, no_fb)); - - // Collinear cases - // 1: segments intersecting are collinear (with a fallback point): - { - point fb(5, 5); - segment col1(point(0, 5), point(5, 5)); - segment col2(point(5, 5), point(10, 5)); // One IP with col1 at (5,5) - segment col3(point(5, 0), point(5, 5)); - BOOST_CHECK_EQUAL( 0, side::apply(col1, col2, col3, fb)); - } - // 2: segment of side calculation collinear with one of the segments - { - point fb(5, 5); - segment col1(point(0, 5), point(10, 5)); - segment col2(point(5, 5), point(5, 12)); // IP with col1 at (5,5) - segment col3(point(5, 1), point(5, 5)); // collinear with col2 - BOOST_CHECK_EQUAL( 0, side::apply(col1, col2, col3, fb)); - } - { - point fb(5, 5); - segment col1(point(10, 5), point(0, 5)); - segment col2(point(5, 5), point(5, 12)); // IP with col1 at (5,5) - segment col3(point(5, 1), point(5, 5)); // collinear with col2 - BOOST_CHECK_EQUAL( 0, side::apply(col1, col2, col3, fb)); - } - { - point fb(5, 5); - segment col1(point(0, 5), point(10, 5)); - segment col2(point(5, 12), point(5, 5)); // IP with col1 at (5,5) - segment col3(point(5, 1), point(5, 5)); // collinear with col2 - BOOST_CHECK_EQUAL( 0, side::apply(col1, col2, col3, fb)); - } - - { - point fb(517248, -517236); - segment col1(point(-172408, -517236), point(862076, -517236)); - segment col2(point(517248, -862064), point(517248, -172408)); - segment col3(point(517248, -172408), point(517248, -517236)); - BOOST_CHECK_EQUAL( 0, side::apply(col1, col2, col3, fb)); - } - { - point fb(-221647, -830336); - segment col1(point(-153817, -837972), point(-222457, -830244)); - segment col2(point(-221139, -833615), point(-290654, -384388)); - segment col3(point(-255266, -814663), point(-264389, -811197)); - BOOST_CHECK_EQUAL(1, side::apply(col1, col2, col3, fb)); // Left of segment... - } - - - { - point fb(27671131, 30809240); - segment col1(point(27671116, 30809247), point(27675474, 30807351)); - segment col2(point(27666779, 30811130), point(27671139, 30809237)); - segment col3(point(27671122, 30809244), point(27675480, 30807348)); - BOOST_CHECK_EQUAL(1, side::apply(col1, col2, col3, fb)); // Left of segment... - } - - // TODO: we might add a check calculating the IP, determining the side - // with the normal side strategy, and verify the results are equal - - return 0; -} -