liuxiaolong
2021-07-20 58d904a328c0d849769b483e901a0be9426b8209
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Boost.Geometry (aka GGL, Generic Geometry Library)
 
// Copyright (c) 2012-2020 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
 
// This file was modified by Oracle on 2016, 2018.
// Modifications copyright (c) 2016-2018 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
 
// 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_TURN_IN_PIECE_VISITOR_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR_HPP
 
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/config.hpp>
 
#include <boost/geometry/algorithms/comparable_distance.hpp>
#include <boost/geometry/algorithms/covered_by.hpp>
#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#include <boost/geometry/geometries/box.hpp>
 
 
namespace boost { namespace geometry
{
 
#ifndef DOXYGEN_NO_DETAIL
 
namespace detail { namespace buffer
{
 
template
<
    typename CsTag,
    typename Turns,
    typename Pieces,
    typename DistanceStrategy
 
>
class turn_in_piece_visitor
{
    Turns& m_turns; // because partition is currently operating on const input only
    Pieces const& m_pieces; // to check for piece-type
    DistanceStrategy const& m_distance_strategy; // to check if point is on original or one_sided
 
    template <typename Operation, typename Piece>
    inline bool skip(Operation const& op, Piece const& piece) const
    {
        if (op.piece_index == piece.index)
        {
            return true;
        }
        Piece const& pc = m_pieces[op.piece_index];
        if (pc.left_index == piece.index || pc.right_index == piece.index)
        {
            if (pc.type == strategy::buffer::buffered_flat_end)
            {
                // If it is a flat end, don't compare against its neighbor:
                // it will always be located on one of the helper segments
                return true;
            }
            if (pc.type == strategy::buffer::buffered_concave)
            {
                // If it is concave, the same applies: the IP will be
                // located on one of the helper segments
                return true;
            }
        }
 
        return false;
    }
 
    template <typename NumericType>
    inline bool is_one_sided(NumericType const& left, NumericType const& right) const
    {
        static NumericType const zero = 0;
        return geometry::math::equals(left, zero)
            || geometry::math::equals(right, zero);
    }
 
    template <typename Point>
    inline bool has_zero_distance_at(Point const& point) const
    {
        return is_one_sided(m_distance_strategy.apply(point, point,
                strategy::buffer::buffer_side_left),
            m_distance_strategy.apply(point, point,
                strategy::buffer::buffer_side_right));
    }
 
public:
 
    inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces,
                                 DistanceStrategy const& distance_strategy)
        : m_turns(turns)
        , m_pieces(pieces)
        , m_distance_strategy(distance_strategy)
    {}
 
    template <typename Turn, typename Piece>
    inline bool apply(Turn const& turn, Piece const& piece)
    {
        if (! turn.is_turn_traversable)
        {
            // Already handled
            return true;
        }
 
        if (piece.type == strategy::buffer::buffered_flat_end
            || piece.type == strategy::buffer::buffered_concave)
        {
            // Turns cannot be located within flat-end or concave pieces
            return true;
        }
 
        if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
        {
            return true;
        }
 
        return apply(turn, piece, piece.m_piece_border);
    }
 
    template <typename Turn, typename Piece, typename Border>
    inline bool apply(Turn const& turn, Piece const& piece, Border const& border)
    {
        if (! geometry::covered_by(turn.point, border.m_envelope))
        {
            // Easy check: if turn is not in the (expanded) envelope
            return true;
        }
 
        if (piece.type == geometry::strategy::buffer::buffered_point)
        {
            // Optimization for a buffer around points: if distance from center
            // is not between min/max radius, it is either inside or outside,
            // and more expensive checks are not necessary.
            typedef typename Border::radius_type radius_type;
 
            radius_type const d = geometry::comparable_distance(piece.m_center, turn.point);
 
            if (d < border.m_min_comparable_radius)
            {
                Turn& mutable_turn = m_turns[turn.turn_index];
                mutable_turn.is_turn_traversable = false;
                return true;
            }
            if (d > border.m_max_comparable_radius)
            {
                return true;
            }
        }
 
        // Check if buffer is one-sided (at this point), because then a point
        // on the original is not considered as within.
        bool const one_sided = has_zero_distance_at(turn.point);
 
        typename Border::state_type state;
        if (! border.point_on_piece(turn.point, one_sided, turn.is_linear_end_point, state))
        {
            return true;
        }
 
        if (state.code() == 1)
        {
            // It is WITHIN a piece, or on the piece border, but not
            // on the offsetted part of it.
 
            // TODO - at further removing rescaling, this threshold can be
            // adapted, or ideally, go.
            // This threshold is minimized to the point where fragile
            // unit tests of hard cases start to fail (5 in multi_polygon)
            // But it is acknowlegded that such a threshold depends on the
            // scale of the input.
            if (state.m_min_distance > 1.0e-5 || ! state.m_close_to_offset)
            {
                Turn& mutable_turn = m_turns[turn.turn_index];
                mutable_turn.is_turn_traversable = false;
 
                // Keep track of the information if this turn was close
                // to an offset (without threshold). Because if it was,
                // it might have been classified incorrectly and in the
                // pretraversal step, it can in hindsight be classified
                // as "outside".
                mutable_turn.close_to_offset = state.m_close_to_offset;
            }
        }
 
        return true;
    }
};
 
 
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
 
 
}} // namespace boost::geometry
 
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR_HPP