File indexing completed on 2025-07-12 08:14:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
0015 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
0016
0017 #include <boost/throw_exception.hpp>
0018
0019 #include <boost/geometry/core/assert.hpp>
0020
0021 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
0022 #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
0023
0024 #include <boost/geometry/util/constexpr.hpp>
0025
0026 namespace boost { namespace geometry {
0027
0028 #ifndef DOXYGEN_NO_DETAIL
0029 namespace detail { namespace overlay {
0030
0031 template<typename AssignPolicy>
0032 struct get_turn_info_linear_linear
0033 {
0034 static const bool handle_spikes = true;
0035
0036 template
0037 <
0038 typename UniqueSubRange1,
0039 typename UniqueSubRange2,
0040 typename TurnInfo,
0041 typename UmbrellaStrategy,
0042 typename RobustPolicy,
0043 typename OutputIterator
0044 >
0045 static inline OutputIterator apply(
0046 UniqueSubRange1 const& range_p,
0047 UniqueSubRange2 const& range_q,
0048 TurnInfo const& tp_model,
0049 UmbrellaStrategy const& umbrella_strategy,
0050 RobustPolicy const& robust_policy,
0051 OutputIterator out)
0052 {
0053 typedef intersection_info
0054 <
0055 UniqueSubRange1, UniqueSubRange2,
0056 typename TurnInfo::point_type,
0057 UmbrellaStrategy,
0058 RobustPolicy
0059 > inters_info;
0060
0061 inters_info inters(range_p, range_q, umbrella_strategy, robust_policy);
0062
0063 char const method = inters.d_info().how;
0064
0065
0066 TurnInfo tp = tp_model;
0067
0068
0069 switch(method)
0070 {
0071 case 'a' :
0072 case 'f' :
0073 case 's' :
0074 get_turn_info_for_endpoint<true, true>
0075 ::apply(range_p, range_q,
0076 tp_model, inters, method_none, out,
0077 umbrella_strategy);
0078 break;
0079
0080 case 'd' :
0081 break;
0082
0083 case 'm' :
0084 {
0085 using handler = touch_interior<TurnInfo, verify_policy_ll>;
0086
0087 if ( get_turn_info_for_endpoint<false, true>
0088 ::apply(range_p, range_q,
0089 tp_model, inters, method_touch_interior, out,
0090 umbrella_strategy) )
0091 {
0092
0093 }
0094 else
0095 {
0096
0097 if ( inters.d_info().arrival[1] == 1)
0098 {
0099 handler::template apply<0>(range_p, range_q, tp,
0100 inters.i_info(), inters.d_info(),
0101 inters.sides(),
0102 umbrella_strategy);
0103 }
0104 else
0105 {
0106
0107 handler::template apply<1>(range_q, range_p, tp,
0108 inters.i_info(), inters.d_info(),
0109 inters.swapped_sides(),
0110 umbrella_strategy);
0111 }
0112
0113 if ( tp.operations[0].operation == operation_blocked )
0114 {
0115 tp.operations[1].is_collinear = true;
0116 }
0117 if ( tp.operations[1].operation == operation_blocked )
0118 {
0119 tp.operations[0].is_collinear = true;
0120 }
0121
0122 replace_method_and_operations_tm(tp.method,
0123 tp.operations[0].operation,
0124 tp.operations[1].operation);
0125
0126 *out++ = tp;
0127 }
0128 }
0129 break;
0130 case 'i' :
0131 {
0132 crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
0133
0134 replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
0135
0136 *out++ = tp;
0137 }
0138 break;
0139 case 't' :
0140 {
0141 using handler = touch<TurnInfo, verify_policy_ll>;
0142
0143
0144 if ( get_turn_info_for_endpoint<false, true>
0145 ::apply(range_p, range_q,
0146 tp_model, inters, method_touch, out,
0147 umbrella_strategy) )
0148 {
0149
0150 }
0151 else
0152 {
0153 handler::apply(range_p, range_q, tp,
0154 inters.i_info(), inters.d_info(),
0155 inters.sides(),
0156 umbrella_strategy);
0157
0158
0159
0160
0161
0162
0163
0164 if ( tp.operations[0].operation == operation_blocked
0165 && tp.operations[1].operation == operation_blocked )
0166 {
0167
0168 if ( inters.is_spike_p() && inters.is_spike_q() )
0169 {
0170 tp.operations[0].operation = operation_union;
0171 tp.operations[1].operation = operation_union;
0172 }
0173 else
0174 {
0175 tp.operations[0].is_collinear = true;
0176 tp.operations[1].is_collinear = true;
0177 }
0178 }
0179 else if ( tp.operations[0].operation == operation_blocked )
0180 {
0181
0182 if ( inters.is_spike_p() )
0183 {
0184 if ( inters.sides().qk_wrt_p1() == 0 )
0185 {
0186 tp.operations[0].is_collinear = true;
0187 }
0188 else
0189 {
0190 tp.operations[0].operation = operation_union;
0191 }
0192 }
0193 else
0194 {
0195 tp.operations[1].is_collinear = true;
0196 }
0197 }
0198 else if ( tp.operations[1].operation == operation_blocked )
0199 {
0200
0201 if ( inters.is_spike_q() )
0202 {
0203 if ( inters.sides().pk_wrt_q1() == 0 )
0204 {
0205 tp.operations[1].is_collinear = true;
0206 }
0207 else
0208 {
0209 tp.operations[1].operation = operation_union;
0210 }
0211 }
0212 else
0213 {
0214 tp.operations[0].is_collinear = true;
0215 }
0216 }
0217 else if ( tp.operations[0].operation == operation_continue
0218 && tp.operations[1].operation == operation_continue )
0219 {
0220
0221 if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
0222 && inters.is_spike_p() )
0223 {
0224 tp.operations[0].operation = operation_union;
0225 tp.operations[1].operation = operation_union;
0226 }
0227 }
0228 else if ( tp.operations[0].operation == operation_none
0229 && tp.operations[1].operation == operation_none )
0230 {
0231
0232 bool const is_p = inters.is_spike_p();
0233 bool const is_q = inters.is_spike_q();
0234
0235 if ( is_p || is_q )
0236 {
0237 tp.operations[0].operation = operation_union;
0238 tp.operations[1].operation = operation_union;
0239
0240 if ( inters.sides().pk_wrt_q2() == 0 )
0241 {
0242 tp.operations[0].operation = operation_continue;
0243 if ( is_p )
0244 {
0245 tp.operations[0].is_collinear = true;
0246 }
0247 }
0248
0249 if ( inters.sides().qk_wrt_p2() == 0 )
0250 {
0251 tp.operations[1].operation = operation_continue;
0252 if ( is_q )
0253 {
0254 tp.operations[1].is_collinear = true;
0255 }
0256 }
0257 }
0258 }
0259
0260
0261
0262 replace_method_and_operations_tm(tp.method,
0263 tp.operations[0].operation,
0264 tp.operations[1].operation);
0265
0266 if BOOST_GEOMETRY_CONSTEXPR (! handle_spikes)
0267 {
0268 *out++ = tp;
0269 }
0270 else if (! append_opposite_spikes<append_touches>(tp, inters, out))
0271 {
0272 *out++ = tp;
0273 }
0274 }
0275 }
0276 break;
0277 case 'e':
0278 {
0279 using handler = equal<TurnInfo, verify_policy_ll>;
0280
0281 if ( get_turn_info_for_endpoint<true, true>
0282 ::apply(range_p, range_q,
0283 tp_model, inters, method_equal, out,
0284 umbrella_strategy) )
0285 {
0286
0287 }
0288 else
0289 {
0290 tp.operations[0].is_collinear = true;
0291 tp.operations[1].is_collinear = true;
0292
0293 if ( ! inters.d_info().opposite )
0294 {
0295
0296
0297 handler::apply(range_p, range_q, tp,
0298 inters.i_info(), inters.d_info(), inters.sides(),
0299 umbrella_strategy);
0300
0301 operation_type spike_op
0302 = ( tp.operations[0].operation != operation_continue
0303 || tp.operations[1].operation != operation_continue ) ?
0304 operation_union :
0305 operation_continue;
0306
0307
0308 turn_transformer_ec transformer(method_touch);
0309 transformer(tp);
0310
0311
0312 if BOOST_GEOMETRY_CONSTEXPR (! handle_spikes)
0313 {
0314 *out++ = tp;
0315 }
0316 else if (! append_collinear_spikes(tp, inters, method_touch,
0317 spike_op, out))
0318 {
0319 *out++ = tp;
0320 }
0321 }
0322 else
0323 {
0324
0325
0326 equal_opposite
0327 <
0328 TurnInfo,
0329 AssignPolicy
0330 >::apply(range_p, range_q, tp, out, inters);
0331 }
0332 }
0333 }
0334 break;
0335 case 'c' :
0336 {
0337
0338 if ( get_turn_info_for_endpoint<true, true>
0339 ::apply(range_p, range_q,
0340 tp_model, inters, method_collinear, out,
0341 umbrella_strategy) )
0342 {
0343
0344 }
0345 else
0346 {
0347
0348 tp.operations[0].is_collinear = true;
0349 tp.operations[1].is_collinear = true;
0350
0351 if ( ! inters.d_info().opposite )
0352 {
0353 method_type method_replace = method_touch_interior;
0354 operation_type spike_op = operation_continue;
0355
0356 if ( inters.d_info().arrival[0] == 0 )
0357 {
0358
0359 using handler = equal<TurnInfo, verify_policy_ll>;
0360
0361 handler::apply(range_p, range_q, tp,
0362 inters.i_info(), inters.d_info(), inters.sides(),
0363 umbrella_strategy);
0364
0365 method_replace = method_touch;
0366 if ( tp.operations[0].operation != operation_continue
0367 || tp.operations[1].operation != operation_continue )
0368 {
0369 spike_op = operation_union;
0370 }
0371 }
0372 else
0373 {
0374 using handler = collinear<TurnInfo, verify_policy_ll>;
0375 handler::apply(range_p, range_q, tp,
0376 inters.i_info(), inters.d_info(),
0377 inters.sides());
0378
0379
0380
0381 }
0382
0383
0384 turn_transformer_ec transformer(method_replace);
0385 transformer(tp);
0386
0387
0388 if BOOST_GEOMETRY_CONSTEXPR (! handle_spikes)
0389 {
0390 *out++ = tp;
0391 }
0392 else if (! append_collinear_spikes(tp, inters, method_replace,
0393 spike_op, out))
0394 {
0395
0396 *out++ = tp;
0397 }
0398 }
0399 else
0400 {
0401
0402 turn_transformer_ec transformer(method_touch_interior);
0403
0404
0405 if BOOST_GEOMETRY_CONSTEXPR (handle_spikes)
0406 {
0407 append_opposite_spikes<append_collinear_opposite>(tp, inters, out);
0408 }
0409
0410
0411
0412
0413
0414 collinear_opposite
0415 <
0416 TurnInfo,
0417 AssignPolicy
0418 >::apply(range_p, range_q,
0419 tp, out, inters, inters.sides(),
0420 transformer);
0421 }
0422 }
0423 }
0424 break;
0425 case '0' :
0426 {
0427
0428 if BOOST_GEOMETRY_CONSTEXPR (AssignPolicy::include_degenerate)
0429 {
0430 only_convert::apply(tp, inters.i_info());
0431
0432
0433 if ( range_p.is_first_segment()
0434 && equals::equals_point_point(range_p.at(0), tp.point, umbrella_strategy) )
0435 {
0436 tp.operations[0].position = position_front;
0437 }
0438 else if ( range_p.is_last_segment()
0439 && equals::equals_point_point(range_p.at(1), tp.point, umbrella_strategy) )
0440 {
0441 tp.operations[0].position = position_back;
0442 }
0443 else if ( range_q.is_first_segment()
0444 && equals::equals_point_point(range_q.at(0), tp.point, umbrella_strategy) )
0445 {
0446 tp.operations[1].position = position_front;
0447 }
0448 else if ( range_q.is_last_segment()
0449 && equals::equals_point_point(range_q.at(1), tp.point, umbrella_strategy) )
0450 {
0451 tp.operations[1].position = position_back;
0452 }
0453
0454 *out++ = tp;
0455 }
0456 }
0457 break;
0458 default :
0459 {
0460 #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
0461 std::cout << "TURN: Unknown method: " << method << std::endl;
0462 #endif
0463 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
0464 BOOST_THROW_EXCEPTION(turn_info_exception(method));
0465 #endif
0466 }
0467 break;
0468 }
0469
0470 return out;
0471 }
0472
0473 template <typename TurnInfo,
0474 typename IntersectionInfo,
0475 typename OutIt>
0476 static inline bool append_collinear_spikes(TurnInfo & tp,
0477 IntersectionInfo const& inters_info,
0478 method_type method, operation_type spike_op,
0479 OutIt out)
0480 {
0481
0482
0483
0484 bool is_p_spike = tp.operations[0].operation == spike_op
0485 && inters_info.is_spike_p();
0486 bool is_q_spike = tp.operations[1].operation == spike_op
0487 && inters_info.is_spike_q();
0488
0489 if ( is_p_spike && is_q_spike )
0490 {
0491 if ( tp.method == method_equal
0492 && tp.operations[0].operation == operation_continue
0493 && tp.operations[1].operation == operation_continue )
0494 {
0495
0496 return false;
0497 }
0498
0499 tp.method = method;
0500 tp.operations[0].operation = operation_blocked;
0501 tp.operations[1].operation = operation_blocked;
0502 *out++ = tp;
0503 tp.operations[0].operation = operation_intersection;
0504 tp.operations[1].operation = operation_intersection;
0505 *out++ = tp;
0506
0507 return true;
0508 }
0509 else if ( is_p_spike )
0510 {
0511 tp.method = method;
0512 tp.operations[0].operation = operation_blocked;
0513 tp.operations[1].operation = operation_union;
0514 *out++ = tp;
0515 tp.operations[0].operation = operation_intersection;
0516
0517 *out++ = tp;
0518
0519 return true;
0520 }
0521 else if ( is_q_spike )
0522 {
0523 tp.method = method;
0524 tp.operations[0].operation = operation_union;
0525 tp.operations[1].operation = operation_blocked;
0526 *out++ = tp;
0527
0528 tp.operations[1].operation = operation_intersection;
0529 *out++ = tp;
0530
0531 return true;
0532 }
0533
0534 return false;
0535 }
0536
0537 enum append_version { append_touches, append_collinear_opposite };
0538
0539 template <append_version Version,
0540 typename TurnInfo,
0541 typename IntersectionInfo,
0542 typename OutIt>
0543 static inline bool append_opposite_spikes(TurnInfo & tp,
0544 IntersectionInfo const& inters,
0545 OutIt out)
0546 {
0547 static const bool is_version_touches = (Version == append_touches);
0548
0549 bool is_p_spike = ( is_version_touches ?
0550 ( tp.operations[0].operation == operation_continue
0551 || tp.operations[0].operation == operation_intersection ) :
0552 true )
0553 && inters.is_spike_p();
0554 bool is_q_spike = ( is_version_touches ?
0555 ( tp.operations[1].operation == operation_continue
0556 || tp.operations[1].operation == operation_intersection ) :
0557 true )
0558 && inters.is_spike_q();
0559
0560 bool res = false;
0561
0562 if (is_p_spike)
0563 {
0564 bool output_spike = false;
0565 if BOOST_GEOMETRY_CONSTEXPR (is_version_touches)
0566 {
0567 tp.operations[0].is_collinear = true;
0568 tp.operations[1].is_collinear = false;
0569 tp.method = method_touch;
0570
0571 output_spike = true;
0572 }
0573 else if (inters.d_info().arrival[0] == 1)
0574 {
0575 tp.operations[0].is_collinear = true;
0576 tp.operations[1].is_collinear = false;
0577
0578 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
0579 base_turn_handler::assign_point(tp, method_touch_interior,
0580 inters.i_info(), 1);
0581
0582 output_spike = true;
0583 }
0584
0585 if (output_spike)
0586 {
0587 tp.operations[0].operation = operation_blocked;
0588 tp.operations[1].operation = operation_intersection;
0589 *out++ = tp;
0590 tp.operations[0].operation = operation_intersection;
0591
0592 *out++ = tp;
0593
0594 res = true;
0595 }
0596 }
0597
0598 if (is_q_spike)
0599 {
0600 bool output_spike = false;
0601 if BOOST_GEOMETRY_CONSTEXPR (is_version_touches)
0602 {
0603 tp.operations[0].is_collinear = false;
0604 tp.operations[1].is_collinear = true;
0605 tp.method = method_touch;
0606
0607 output_spike = true;
0608 }
0609 else if (inters.d_info().arrival[1] == 1)
0610 {
0611 tp.operations[0].is_collinear = false;
0612 tp.operations[1].is_collinear = true;
0613
0614 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 0);
0615 base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0);
0616
0617 output_spike = true;
0618 }
0619
0620 if (output_spike)
0621 {
0622 tp.operations[0].operation = operation_intersection;
0623 tp.operations[1].operation = operation_blocked;
0624 *out++ = tp;
0625
0626 tp.operations[1].operation = operation_intersection;
0627 *out++ = tp;
0628
0629 res = true;
0630 }
0631 }
0632
0633 return res;
0634 }
0635
0636 static inline void replace_method_and_operations_tm(method_type & method,
0637 operation_type & op0,
0638 operation_type & op1)
0639 {
0640 if ( op0 == operation_blocked && op1 == operation_blocked )
0641 {
0642
0643 method = (method == method_touch ? method_equal : method_collinear);
0644 op0 = operation_continue;
0645 op1 = operation_continue;
0646 }
0647 else
0648 {
0649 if ( op0 == operation_continue || op0 == operation_blocked )
0650 {
0651 op0 = operation_intersection;
0652 }
0653 else if ( op0 == operation_intersection )
0654 {
0655 op0 = operation_union;
0656 }
0657
0658 if ( op1 == operation_continue || op1 == operation_blocked )
0659 {
0660 op1 = operation_intersection;
0661 }
0662 else if ( op1 == operation_intersection )
0663 {
0664 op1 = operation_union;
0665 }
0666
0667
0668 if ( method == method_error )
0669 {
0670 method = method_touch_interior;
0671 op0 = operation_union;
0672 op1 = operation_union;
0673 }
0674 }
0675 }
0676
0677 class turn_transformer_ec
0678 {
0679 public:
0680 explicit turn_transformer_ec(method_type method_t_or_m)
0681 : m_method(method_t_or_m)
0682 {}
0683
0684 template <typename Turn>
0685 void operator()(Turn & turn) const
0686 {
0687 operation_type & op0 = turn.operations[0].operation;
0688 operation_type & op1 = turn.operations[1].operation;
0689
0690 BOOST_GEOMETRY_ASSERT(op0 != operation_blocked || op1 != operation_blocked );
0691
0692 if ( op0 == operation_blocked )
0693 {
0694 op0 = operation_intersection;
0695 }
0696 else if ( op0 == operation_intersection )
0697 {
0698 op0 = operation_union;
0699 }
0700
0701 if ( op1 == operation_blocked )
0702 {
0703 op1 = operation_intersection;
0704 }
0705 else if ( op1 == operation_intersection )
0706 {
0707 op1 = operation_union;
0708 }
0709
0710 if ( op0 == operation_intersection || op0 == operation_union
0711 || op1 == operation_intersection || op1 == operation_union )
0712 {
0713 turn.method = m_method;
0714 }
0715
0716
0717
0718 turn.operations[0].is_collinear = op0 != operation_intersection;
0719 turn.operations[1].is_collinear = op1 != operation_intersection;
0720 }
0721
0722 private:
0723 method_type m_method;
0724 };
0725
0726 static inline void replace_operations_i(operation_type & op0, operation_type & op1)
0727 {
0728 if ( op0 == operation_intersection )
0729 {
0730 op0 = operation_union;
0731 }
0732
0733 if ( op1 == operation_intersection )
0734 {
0735 op1 = operation_union;
0736 }
0737 }
0738 };
0739
0740 }}
0741 #endif
0742
0743 }}
0744
0745 #endif