1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // This file was modified by Oracle on 2020.
6 // Modifications copyright (c) 2020 Oracle and/or its affiliates.
7 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
8 
9 // Use, modification and distribution is subject to the Boost Software License,
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
12 
13 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP
14 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP
15 
16 
17 #include <boost/range/size.hpp>
18 #include <boost/range/value_type.hpp>
19 
20 #include <boost/geometry/core/assert.hpp>
21 #include <boost/geometry/core/exterior_ring.hpp>
22 #include <boost/geometry/core/interior_rings.hpp>
23 #include <boost/geometry/core/ring_type.hpp>
24 #include <boost/geometry/core/tags.hpp>
25 #include <boost/geometry/algorithms/detail/ring_identifier.hpp>
26 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
27 #include <boost/geometry/algorithms/num_points.hpp>
28 #include <boost/geometry/geometries/concepts/check.hpp>
29 #include <boost/geometry/util/range.hpp>
30 
31 
32 namespace boost { namespace geometry
33 {
34 
35 
36 #ifndef DOXYGEN_NO_DETAIL
37 namespace detail { namespace overlay
38 {
39 
40 
41 template<typename Tag>
42 struct get_ring
43 {};
44 
45 // A range of rings (multi-ring but that does not exist)
46 // gets the "void" tag and is dispatched here.
47 template<>
48 struct get_ring<void>
49 {
50     template<typename Range>
51     static inline typename boost::range_value<Range>::type const&
applyboost::geometry::detail::overlay::get_ring52                 apply(ring_identifier const& id, Range const& container)
53     {
54         return range::at(container, id.multi_index);
55     }
56 };
57 
58 
59 template<>
60 struct get_ring<ring_tag>
61 {
62     template<typename Ring>
applyboost::geometry::detail::overlay::get_ring63     static inline Ring const& apply(ring_identifier const& , Ring const& ring)
64     {
65         return ring;
66     }
67 };
68 
69 
70 template<>
71 struct get_ring<box_tag>
72 {
73     template<typename Box>
applyboost::geometry::detail::overlay::get_ring74     static inline Box const& apply(ring_identifier const& ,
75                     Box const& box)
76     {
77         return box;
78     }
79 };
80 
81 
82 template<>
83 struct get_ring<polygon_tag>
84 {
85     template<typename Polygon>
applyboost::geometry::detail::overlay::get_ring86     static inline typename ring_return_type<Polygon const>::type const apply(
87                 ring_identifier const& id,
88                 Polygon const& polygon)
89     {
90         BOOST_GEOMETRY_ASSERT
91             (
92                 id.ring_index >= -1
93                 && id.ring_index < int(boost::size(interior_rings(polygon)))
94             );
95         return id.ring_index < 0
96             ? exterior_ring(polygon)
97             : range::at(interior_rings(polygon), id.ring_index);
98     }
99 };
100 
101 
102 template<>
103 struct get_ring<multi_polygon_tag>
104 {
105     template<typename MultiPolygon>
applyboost::geometry::detail::overlay::get_ring106     static inline typename ring_type<MultiPolygon>::type const& apply(
107                 ring_identifier const& id,
108                 MultiPolygon const& multi_polygon)
109     {
110         BOOST_GEOMETRY_ASSERT
111             (
112                 id.multi_index >= 0
113                 && id.multi_index < int(boost::size(multi_polygon))
114             );
115         return get_ring<polygon_tag>::apply(id,
116                     range::at(multi_polygon, id.multi_index));
117     }
118 };
119 
120 // Returns the number of segments on a ring (regardless whether the ring is open or closed)
121 template <typename Geometry>
segment_count_on_ring(Geometry const & geometry,ring_identifier const & ring_id)122 inline signed_size_type segment_count_on_ring(Geometry const& geometry,
123                                               ring_identifier const& ring_id)
124 {
125     using tag = typename geometry::tag<Geometry>::type;
126 
127     // A closed polygon, a triangle of 4 points, including starting point,
128     // contains 3 segments. So handle as if it is closed, and subtract one.
129     return geometry::num_points(detail::overlay::get_ring<tag>::apply(ring_id, geometry), true) - 1;
130 }
131 
132 // Returns the number of segments on a ring (regardless whether the ring is open or closed)
133 template <typename Geometry>
segment_count_on_ring(Geometry const & geometry,segment_identifier const & seg_id)134 inline signed_size_type segment_count_on_ring(Geometry const& geometry,
135                                               segment_identifier const& seg_id)
136 {
137     return segment_count_on_ring(geometry, ring_identifier(0, seg_id.multi_index, seg_id.ring_index));
138 }
139 
140 
141 // Returns the distance between the second and the first segment identifier (second-first)
142 // It supports circular behavior and for this it is necessary to pass the geometry.
143 // It will not report negative values
144 template <typename Geometry>
segment_distance(Geometry const & geometry,segment_identifier const & first,segment_identifier const & second)145 inline signed_size_type segment_distance(Geometry const& geometry,
146         segment_identifier const& first, segment_identifier const& second)
147 {
148     // It is an internal function, make sure the preconditions are met
149     BOOST_ASSERT(second.source_index == first.source_index);
150     BOOST_ASSERT(second.multi_index == first.multi_index);
151     BOOST_ASSERT(second.ring_index == first.ring_index);
152 
153     signed_size_type const result = second.segment_index - first.segment_index;
154     if (second.segment_index >= first.segment_index)
155     {
156         return result;
157     }
158     // Take wrap into account, counting segments on the ring (passing any of the ids is fine).
159     // Suppose point_count=10 (10 points, 9 segments), first.seg_id=7, second.seg_id=2,
160     // then distance=9-7+2=4, being segments 7,8,0,1
161     return segment_count_on_ring(geometry, first) + result;
162 }
163 
164 }} // namespace detail::overlay
165 #endif // DOXYGEN_NO_DETAIL
166 
167 
168 }} // namespace boost::geometry
169 
170 
171 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP
172