File indexing completed on 2026-08-21 08:45:50
0001
0002
0003
0004
0005
0006
0007
0008
0009 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_EDGE_HPP
0010 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_EDGE_HPP
0011
0012 #include <boost/core/ignore_unused.hpp>
0013 #include <boost/geometry/algorithms/detail/overlay/approximately_equals.hpp>
0014 #include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
0015 #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
0016 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
0017 #include <boost/geometry/algorithms/detail/overlay/turn_operation_id.hpp>
0018 #include <boost/geometry/algorithms/detail/overlay/graph/node_util.hpp>
0019 #include <boost/geometry/algorithms/detail/overlay/graph/select_toi_by_incoming.hpp>
0020
0021 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0022 #include <boost/geometry/io/wkt/wkt.hpp>
0023 #endif
0024
0025 namespace boost { namespace geometry
0026 {
0027
0028 #ifndef DOXYGEN_NO_DETAIL
0029 namespace detail { namespace overlay
0030 {
0031
0032 template <typename Point>
0033 struct edge_and_side
0034 {
0035 turn_operation_id toi{0};
0036 Point point{};
0037 int side{0};
0038 };
0039
0040 template
0041 <
0042 bool Reverse1,
0043 bool Reverse2,
0044 overlay_type OverlayType,
0045 typename Geometry1,
0046 typename Geometry2,
0047 typename Turns,
0048 typename Clusters,
0049 typename Strategy
0050 >
0051 struct edge_selector
0052 {
0053 private:
0054 static constexpr operation_type target_operation = operation_from_overlay<OverlayType>::value;
0055 using point_type = typename Turns::value_type::point_type;
0056 using edge_type = edge_and_side<point_type>;
0057 using edges_type = std::vector<edge_type>;
0058
0059
0060 using coor_type = typename geometry::select_most_precise
0061 <
0062 geometry::coordinate_type_t<point_type>,
0063 double
0064 >::type;
0065
0066
0067
0068
0069 template <typename Operation>
0070 point_type walk_to_point_after_turn(Operation const& op, point_type const& turn_point) const
0071 {
0072 static const coor_type tolerance
0073 = common_approximately_equals_epsilon_multiplier<coor_type>::value();
0074 int offset = 1;
0075 point_type point;
0076 do
0077 {
0078 geometry::copy_segment_point<Reverse1, Reverse2>(m_geometry1, m_geometry2,
0079 op.seg_id, offset, point);
0080 ++offset;
0081 } while (approximately_equals(point, turn_point, tolerance) && offset < 10);
0082 return point;
0083 }
0084
0085
0086
0087
0088
0089 bool select_collinear_target_edge(edge_type const& a, edge_type const& b) const
0090 {
0091 auto const& turn_a = m_turns[a.toi.turn_index];
0092 auto const& turn_b = m_turns[b.toi.turn_index];
0093 auto const& op_a = turn_a.operations[a.toi.operation_index];
0094 auto const& op_b = turn_b.operations[b.toi.operation_index];
0095
0096 auto const target_a = get_node_id(m_turns, op_a.enriched.travels_to_ip_index);
0097 auto const target_b = get_node_id(m_turns, op_b.enriched.travels_to_ip_index);
0098
0099 auto const& other_op_a = turn_a.operations[1 - a.toi.operation_index];
0100 auto const& other_op_b = turn_b.operations[1 - b.toi.operation_index];
0101
0102 if (other_op_a.enriched.travels_to_ip_index == -1)
0103 {
0104 return true;
0105 }
0106 if (other_op_b.enriched.travels_to_ip_index == -1)
0107 {
0108 return false;
0109 }
0110
0111 auto const other_target_a = get_node_id(m_turns, other_op_a.enriched.travels_to_ip_index);
0112 auto const other_target_b = get_node_id(m_turns, other_op_b.enriched.travels_to_ip_index);
0113
0114 if (target_b == other_target_a || target_b == other_target_b)
0115 {
0116
0117 return false;
0118 }
0119 if (target_a == other_target_a || target_a == other_target_b)
0120 {
0121
0122 return true;
0123 }
0124
0125 return true;
0126 }
0127
0128 void report(const char* caption, edges_type const& edges,
0129 point_type const& p1, point_type const& p2) const
0130 {
0131 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0132 std::cout << " *** Sorted edges " << caption
0133 << " from " << geometry::wkt(p1) << " to " << geometry::wkt(p2)
0134 << std::endl;
0135 for (auto const& item : edges)
0136 {
0137 auto const& op = m_turns[item.toi.turn_index].operations[item.toi.operation_index];
0138 std::cout << " -> " << item.toi
0139 << " to " << op.enriched.travels_to_ip_index
0140 << " side: " << item.side
0141 << std::endl;
0142 }
0143 #endif
0144 }
0145
0146 turn_operation_id select_by_side(edges_type& edges, point_type const& p1, point_type const& p2) const
0147 {
0148
0149 auto const side_strategy = m_intersection_strategy.side();
0150 for (auto& edge : edges)
0151 {
0152 auto const& op = m_turns[edge.toi.turn_index].operations[edge.toi.operation_index];
0153 edge.point = walk_to_point_after_turn(op, p2);
0154 edge.side = side_strategy.apply(p1, p2, edge.point);
0155 }
0156
0157
0158
0159
0160
0161 std::sort(edges.begin(), edges.end(), [](auto const& a, auto const& b)
0162 {
0163 return std::tie(a.side, a.toi) < std::tie(b.side, b.toi);
0164 });
0165
0166 report("by side", edges, p1, p2);
0167
0168 if (edges.size() == 1 || (edges.size() > 1 && edges.front().side != edges[1].side))
0169 {
0170 return edges.front().toi;
0171 }
0172
0173 if (edges.front().side != edges.back().side)
0174 {
0175
0176 auto it = std::find_if(edges.begin() + 1, edges.end(), [&](auto const& item)
0177 {
0178 return item.side != edges.front().side;
0179 });
0180 edges.erase(it, edges.end());
0181 }
0182
0183 if (edges.front().side == 0)
0184 {
0185
0186 auto compare = [&](edge_type const& a, edge_type const& b) -> bool
0187 {
0188 return select_collinear_target_edge(a, b);
0189 };
0190 std::sort(edges.begin(), edges.end(), compare);
0191 return edges.front().toi;
0192 }
0193
0194
0195 auto compare_one_side = [&](auto const& a, auto const& b) -> bool
0196 {
0197
0198 int const side = side_strategy.apply(p2, a.point, b.point);
0199 return side == 1;
0200 };
0201
0202 std::sort(edges.begin(), edges.end(), compare_one_side);
0203
0204 report("by mutual side", edges, p1, p2);
0205
0206 return edges.front().toi;
0207 }
0208
0209 public:
0210
0211 edge_selector(Geometry1 const& m_geometry1, Geometry2 const& m_geometry2,
0212 Turns const& m_turns, Clusters const& clusters,
0213 Strategy const& strategy)
0214 : m_geometry1(m_geometry1)
0215 , m_geometry2(m_geometry2)
0216 , m_turns(m_turns)
0217 , m_clusters(clusters)
0218 , m_intersection_strategy(strategy)
0219 {}
0220
0221
0222
0223
0224
0225 turn_operation_id select_target_edge(set_of_tois const& turn_operation_ids,
0226 point_type const& p1, point_type const& p2) const
0227 {
0228 if (turn_operation_ids.empty())
0229 {
0230 return {};
0231 }
0232 if (turn_operation_ids.size() == 1)
0233 {
0234 return *turn_operation_ids.begin();
0235 }
0236
0237 edges_type edges;
0238 edges.reserve(turn_operation_ids.size());
0239 for (auto const& toi : turn_operation_ids)
0240 {
0241 edges.emplace_back(edge_type{toi});
0242 }
0243
0244
0245 auto assert_one_cluster = [&]() -> bool
0246 {
0247 auto const& turn0 = m_turns[edges[0].toi.turn_index];
0248 auto const cluster_id = turn0.cluster_id;
0249 for (auto const& toi : turn_operation_ids)
0250 {
0251 auto const& turn = m_turns[toi.turn_index];
0252 if (turn.cluster_id != cluster_id)
0253 {
0254 return false;
0255 }
0256 }
0257 return true;
0258 };
0259
0260 boost::ignore_unused(assert_one_cluster);
0261
0262
0263
0264 if (edges.size() == 2)
0265 {
0266 auto const& turn0 = m_turns[edges[0].toi.turn_index];
0267 auto const& turn1 = m_turns[edges[1].toi.turn_index];
0268 auto const& op0 = turn0.operations[edges[0].toi.operation_index];
0269 auto const& op1 = turn1.operations[edges[1].toi.operation_index];
0270 if (op0.operation == operation_continue
0271 && op1.operation == operation_continue
0272 && op0.enriched.travels_to_ip_index == op1.enriched.travels_to_ip_index)
0273 {
0274 return edges.front().toi;
0275 }
0276
0277 if (target_operation == operation_union
0278 && turn0.is_clustered()
0279 && op0.operation == operation_union
0280 && op1.operation == operation_union
0281 && op0.enriched.rank == op1.enriched.rank)
0282 {
0283
0284
0285 BOOST_GEOMETRY_ASSERT(assert_one_cluster());
0286
0287 turn_operation_id result;
0288 if (select_toi_for_union(result, op0, op1, edges[0].toi, edges[1].toi, m_turns))
0289 {
0290 return result;
0291 }
0292
0293 bool const better = is_better_collinear_for_union(
0294 op0, op1, edges.front().toi, edges.back().toi);
0295 return better ? edges.front().toi : edges.back().toi;
0296 }
0297 }
0298
0299 return select_by_side(edges, p1, p2);
0300 }
0301
0302 private:
0303 Geometry1 const& m_geometry1;
0304 Geometry2 const& m_geometry2;
0305 Turns const& m_turns;
0306 Clusters const& m_clusters;
0307 Strategy const& m_intersection_strategy;
0308 };
0309
0310 }}
0311 #endif
0312
0313 }}
0314
0315 #endif