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