1 // Boost.Geometry (aka GGL, Generic Geometry Library) 2 3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. 4 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. 5 6 // This file was modified by Oracle on 2013, 2014, 2015, 2017. 7 // Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. 8 9 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle 10 11 // Use, modification and distribution is subject to the Boost Software License, 12 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 13 // http://www.boost.org/LICENSE_1_0.txt) 14 15 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP 16 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP 17 18 #include <boost/throw_exception.hpp> 19 20 #include <boost/geometry/core/assert.hpp> 21 22 #include <boost/geometry/util/condition.hpp> 23 24 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> 25 #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp> 26 27 // TEMP, for spikes detector 28 //#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp> 29 30 namespace boost { namespace geometry { 31 32 #ifndef DOXYGEN_NO_DETAIL 33 namespace detail { namespace overlay { 34 35 template<typename AssignPolicy> 36 struct get_turn_info_linear_areal 37 { 38 // Currently only Linear spikes are handled 39 // Areal spikes are ignored 40 static const bool handle_spikes = true; 41 42 template 43 < 44 typename Point1, 45 typename Point2, 46 typename TurnInfo, 47 typename IntersectionStrategy, 48 typename RobustPolicy, 49 typename OutputIterator 50 > applyboost::geometry::detail::overlay::get_turn_info_linear_areal51 static inline OutputIterator apply( 52 Point1 const& pi, Point1 const& pj, Point1 const& pk, 53 Point2 const& qi, Point2 const& qj, Point2 const& qk, 54 bool is_p_first, bool is_p_last, 55 bool is_q_first, bool is_q_last, 56 TurnInfo const& tp_model, 57 IntersectionStrategy const& intersection_strategy, 58 RobustPolicy const& robust_policy, 59 OutputIterator out) 60 { 61 typedef intersection_info 62 < 63 Point1, Point2, 64 typename TurnInfo::point_type, 65 IntersectionStrategy, 66 RobustPolicy 67 > inters_info; 68 69 inters_info inters(pi, pj, pk, qi, qj, qk, intersection_strategy, robust_policy); 70 71 char const method = inters.d_info().how; 72 73 // Copy, to copy possibly extended fields 74 TurnInfo tp = tp_model; 75 76 // Select method and apply 77 switch(method) 78 { 79 case 'a' : // collinear, "at" 80 case 'f' : // collinear, "from" 81 case 's' : // starts from the middle 82 get_turn_info_for_endpoint<true, true>( 83 pi, pj, pk, qi, qj, qk, 84 is_p_first, is_p_last, is_q_first, is_q_last, 85 tp_model, inters, method_none, out); 86 break; 87 88 case 'd' : // disjoint: never do anything 89 break; 90 91 case 'm' : 92 { 93 if ( get_turn_info_for_endpoint<false, true>( 94 pi, pj, pk, qi, qj, qk, 95 is_p_first, is_p_last, is_q_first, is_q_last, 96 tp_model, inters, method_touch_interior, out) ) 97 { 98 // do nothing 99 } 100 else 101 { 102 typedef touch_interior 103 < 104 TurnInfo 105 > policy; 106 107 // If Q (1) arrives (1) 108 if ( inters.d_info().arrival[1] == 1 ) 109 { 110 policy::template apply<0>(pi, pj, pk, qi, qj, qk, 111 tp, inters.i_info(), inters.d_info(), 112 inters.sides()); 113 } 114 else 115 { 116 // Swap p/q 117 side_calculator 118 < 119 typename inters_info::cs_tag, 120 typename inters_info::robust_point2_type, 121 typename inters_info::robust_point1_type, 122 typename inters_info::side_strategy_type 123 > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), 124 inters.rpi(), inters.rpj(), inters.rpk(), 125 inters.get_side_strategy()); 126 policy::template apply<1>(qi, qj, qk, pi, pj, pk, 127 tp, inters.i_info(), inters.d_info(), 128 swapped_side_calc); 129 } 130 131 if ( tp.operations[1].operation == operation_blocked ) 132 { 133 tp.operations[0].is_collinear = true; 134 } 135 136 replace_method_and_operations_tm(tp.method, 137 tp.operations[0].operation, 138 tp.operations[1].operation); 139 140 // this function assumes that 'u' must be set for a spike 141 calculate_spike_operation(tp.operations[0].operation, 142 inters, is_p_last); 143 144 AssignPolicy::apply(tp, pi, qi, inters); 145 146 *out++ = tp; 147 } 148 } 149 break; 150 case 'i' : 151 { 152 crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, 153 tp, inters.i_info(), inters.d_info()); 154 155 replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); 156 157 AssignPolicy::apply(tp, pi, qi, inters); 158 *out++ = tp; 159 } 160 break; 161 case 't' : 162 { 163 // Both touch (both arrive there) 164 if ( get_turn_info_for_endpoint<false, true>( 165 pi, pj, pk, qi, qj, qk, 166 is_p_first, is_p_last, is_q_first, is_q_last, 167 tp_model, inters, method_touch, out) ) 168 { 169 // do nothing 170 } 171 else 172 { 173 touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, 174 tp, inters.i_info(), inters.d_info(), inters.sides()); 175 176 if ( tp.operations[1].operation == operation_blocked ) 177 { 178 tp.operations[0].is_collinear = true; 179 } 180 181 // workarounds for touch<> not taking spikes into account starts here 182 // those was discovered empirically 183 // touch<> is not symmetrical! 184 // P spikes and Q spikes may produce various operations! 185 // Only P spikes are valid for L/A 186 // TODO: this is not optimal solution - think about rewriting touch<> 187 188 if ( tp.operations[0].operation == operation_blocked ) 189 { 190 // a spike on P on the same line with Q1 191 if ( inters.is_spike_p() ) 192 { 193 if ( inters.sides().qk_wrt_p1() == 0 ) 194 { 195 tp.operations[0].is_collinear = true; 196 } 197 else 198 { 199 tp.operations[0].operation = operation_union; 200 } 201 } 202 } 203 else if ( tp.operations[0].operation == operation_continue 204 && tp.operations[1].operation == operation_continue ) 205 { 206 // P spike on the same line with Q2 (opposite) 207 if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() 208 && inters.is_spike_p() ) 209 { 210 tp.operations[0].operation = operation_union; 211 tp.operations[1].operation = operation_union; 212 } 213 } 214 else if ( tp.operations[0].operation == operation_none 215 && tp.operations[1].operation == operation_none ) 216 { 217 // spike not handled by touch<> 218 if ( inters.is_spike_p() ) 219 { 220 tp.operations[0].operation = operation_intersection; 221 tp.operations[1].operation = operation_union; 222 223 if ( inters.sides().pk_wrt_q2() == 0 ) 224 { 225 tp.operations[0].operation = operation_continue; // will be converted to i 226 tp.operations[0].is_collinear = true; 227 } 228 } 229 } 230 231 // workarounds for touch<> not taking spikes into account ends here 232 233 replace_method_and_operations_tm(tp.method, 234 tp.operations[0].operation, 235 tp.operations[1].operation); 236 237 bool ignore_spike 238 = calculate_spike_operation(tp.operations[0].operation, 239 inters, is_p_last); 240 241 // TODO: move this into the append_xxx and call for each turn? 242 AssignPolicy::apply(tp, pi, qi, inters); 243 244 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) 245 || ignore_spike 246 || ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i??? 247 tp, inters, is_p_last, is_q_last, out) ) 248 { 249 *out++ = tp; 250 } 251 } 252 } 253 break; 254 case 'e': 255 { 256 if ( get_turn_info_for_endpoint<true, true>( 257 pi, pj, pk, qi, qj, qk, 258 is_p_first, is_p_last, is_q_first, is_q_last, 259 tp_model, inters, method_equal, out) ) 260 { 261 // do nothing 262 } 263 else 264 { 265 tp.operations[0].is_collinear = true; 266 267 if ( ! inters.d_info().opposite ) 268 { 269 // Both equal 270 // or collinear-and-ending at intersection point 271 equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, 272 tp, inters.i_info(), inters.d_info(), inters.sides()); 273 274 turn_transformer_ec<false> transformer(method_touch); 275 transformer(tp); 276 277 // TODO: move this into the append_xxx and call for each turn? 278 AssignPolicy::apply(tp, pi, qi, inters); 279 280 // conditionally handle spikes 281 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) 282 || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, 283 method_touch, append_equal, out) ) 284 { 285 *out++ = tp; // no spikes 286 } 287 } 288 else 289 { 290 equal_opposite 291 < 292 TurnInfo, 293 AssignPolicy 294 >::apply(pi, qi, 295 tp, out, inters); 296 } 297 } 298 } 299 break; 300 case 'c' : 301 { 302 // Collinear 303 if ( get_turn_info_for_endpoint<true, true>( 304 pi, pj, pk, qi, qj, qk, 305 is_p_first, is_p_last, is_q_first, is_q_last, 306 tp_model, inters, method_collinear, out) ) 307 { 308 // do nothing 309 } 310 else 311 { 312 tp.operations[0].is_collinear = true; 313 314 if ( ! inters.d_info().opposite ) 315 { 316 method_type method_replace = method_touch_interior; 317 append_version_c version = append_collinear; 318 319 if ( inters.d_info().arrival[0] == 0 ) 320 { 321 // Collinear, but similar thus handled as equal 322 equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, 323 tp, inters.i_info(), inters.d_info(), inters.sides()); 324 325 method_replace = method_touch; 326 version = append_equal; 327 } 328 else 329 { 330 collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, 331 tp, inters.i_info(), inters.d_info(), inters.sides()); 332 333 //method_replace = method_touch_interior; 334 //version = append_collinear; 335 } 336 337 turn_transformer_ec<false> transformer(method_replace); 338 transformer(tp); 339 340 // TODO: move this into the append_xxx and call for each turn? 341 AssignPolicy::apply(tp, pi, qi, inters); 342 343 // conditionally handle spikes 344 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) 345 || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, 346 method_replace, version, out) ) 347 { 348 // no spikes 349 *out++ = tp; 350 } 351 } 352 else 353 { 354 // Is this always 'm' ? 355 turn_transformer_ec<false> transformer(method_touch_interior); 356 357 // conditionally handle spikes 358 if ( BOOST_GEOMETRY_CONDITION(handle_spikes) ) 359 { 360 append_opposite_spikes<append_collinear_opposite>( 361 tp, inters, is_p_last, is_q_last, out); 362 } 363 364 // TODO: ignore for spikes? 365 // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, 366 // the same with is_q_valid 367 368 collinear_opposite 369 < 370 TurnInfo, 371 AssignPolicy 372 >::apply(pi, pj, pk, qi, qj, qk, 373 tp, out, inters, 374 inters.sides(), transformer, 375 !is_p_last, true); // qk is always valid 376 } 377 } 378 } 379 break; 380 case '0' : 381 { 382 // degenerate points 383 if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) ) 384 { 385 only_convert::apply(tp, inters.i_info()); 386 387 if ( is_p_first 388 && equals::equals_point_point(pi, tp.point) ) 389 { 390 tp.operations[0].position = position_front; 391 } 392 else if ( is_p_last 393 && equals::equals_point_point(pj, tp.point) ) 394 { 395 tp.operations[0].position = position_back; 396 } 397 // tp.operations[1].position = position_middle; 398 399 AssignPolicy::apply(tp, pi, qi, inters); 400 *out++ = tp; 401 } 402 } 403 break; 404 default : 405 { 406 #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) 407 std::cout << "TURN: Unknown method: " << method << std::endl; 408 #endif 409 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) 410 BOOST_THROW_EXCEPTION(turn_info_exception(method)); 411 #endif 412 } 413 break; 414 } 415 416 return out; 417 } 418 419 template <typename Operation, 420 typename IntersectionInfo> calculate_spike_operationboost::geometry::detail::overlay::get_turn_info_linear_areal421 static inline bool calculate_spike_operation(Operation & op, 422 IntersectionInfo const& inters, 423 bool is_p_last) 424 { 425 bool is_p_spike = ( op == operation_union || op == operation_intersection ) 426 && ! is_p_last 427 && inters.is_spike_p(); 428 429 if ( is_p_spike ) 430 { 431 int const pk_q1 = inters.sides().pk_wrt_q1(); 432 433 bool going_in = pk_q1 < 0; // Pk on the right 434 bool going_out = pk_q1 > 0; // Pk on the left 435 436 int const qk_q1 = inters.sides().qk_wrt_q1(); 437 438 // special cases 439 if ( qk_q1 < 0 ) // Q turning R 440 { 441 // spike on the edge point 442 // if it's already known that the spike is going out this musn't be checked 443 if ( ! going_out 444 && equals::equals_point_point(inters.rpj(), inters.rqj()) ) 445 { 446 int const pk_q2 = inters.sides().pk_wrt_q2(); 447 going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both 448 going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them 449 } 450 } 451 else if ( qk_q1 > 0 ) // Q turning L 452 { 453 // spike on the edge point 454 // if it's already known that the spike is going in this musn't be checked 455 if ( ! going_in 456 && equals::equals_point_point(inters.rpj(), inters.rqj()) ) 457 { 458 int const pk_q2 = inters.sides().pk_wrt_q2(); 459 going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them 460 going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both 461 } 462 } 463 464 if ( going_in ) 465 { 466 op = operation_intersection; 467 return true; 468 } 469 else if ( going_out ) 470 { 471 op = operation_union; 472 return true; 473 } 474 } 475 476 return false; 477 } 478 479 enum append_version_c { append_equal, append_collinear }; 480 481 template <typename TurnInfo, 482 typename IntersectionInfo, 483 typename OutIt> append_collinear_spikesboost::geometry::detail::overlay::get_turn_info_linear_areal484 static inline bool append_collinear_spikes(TurnInfo & tp, 485 IntersectionInfo const& inters, 486 bool is_p_last, bool /*is_q_last*/, 487 method_type method, append_version_c version, 488 OutIt out) 489 { 490 // method == touch || touch_interior 491 // both position == middle 492 493 bool is_p_spike = ( version == append_equal ? 494 ( tp.operations[0].operation == operation_union 495 || tp.operations[0].operation == operation_intersection ) : 496 tp.operations[0].operation == operation_continue ) 497 && ! is_p_last 498 && inters.is_spike_p(); 499 500 // TODO: throw an exception for spike in Areal? 501 /*bool is_q_spike = tp.operations[1].operation == operation_continue 502 && inters.is_spike_q(); 503 504 // both are collinear spikes on the same IP, we can just follow both 505 if ( is_p_spike && is_q_spike ) 506 { 507 return false; 508 } 509 // spike on Linear - it's turning back on the boundary of Areal 510 else*/ 511 if ( is_p_spike ) 512 { 513 tp.method = method; 514 tp.operations[0].operation = operation_blocked; 515 tp.operations[1].operation = operation_union; 516 *out++ = tp; 517 tp.operations[0].operation = operation_continue; // boundary 518 //tp.operations[1].operation = operation_union; 519 *out++ = tp; 520 521 return true; 522 } 523 // spike on Areal - Linear is going outside 524 /*else if ( is_q_spike ) 525 { 526 tp.method = method; 527 tp.operations[0].operation = operation_union; 528 tp.operations[1].operation = operation_continue; 529 *out++ = tp; 530 *out++ = tp; 531 532 return true; 533 }*/ 534 535 return false; 536 } 537 538 enum append_version_o { append_touches, append_collinear_opposite }; 539 540 template <append_version_o Version, 541 typename TurnInfo, 542 typename IntersectionInfo, 543 typename OutIt> append_opposite_spikesboost::geometry::detail::overlay::get_turn_info_linear_areal544 static inline bool append_opposite_spikes(TurnInfo & tp, 545 IntersectionInfo const& inters, 546 bool is_p_last, bool /*is_q_last*/, 547 OutIt out) 548 { 549 static const bool is_version_touches = (Version == append_touches); 550 551 bool is_p_spike = ( is_version_touches ? 552 ( tp.operations[0].operation == operation_continue 553 || tp.operations[0].operation == operation_intersection ) : // i ??? 554 true ) 555 && ! is_p_last 556 && inters.is_spike_p(); 557 558 // TODO: throw an exception for spike in Areal? 559 /*bool is_q_spike = ( ( Version == append_touches 560 && tp.operations[1].operation == operation_continue ) 561 || ( Version == append_collinear_opposite 562 && tp.operations[1].operation == operation_none ) ) 563 && inters.is_spike_q(); 564 565 if ( is_p_spike && is_q_spike ) 566 { 567 // u/u or nothing? 568 return false; 569 } 570 else*/ 571 if ( is_p_spike ) 572 { 573 if ( BOOST_GEOMETRY_CONDITION(is_version_touches) 574 || inters.d_info().arrival[0] == 1 ) 575 { 576 if ( BOOST_GEOMETRY_CONDITION(is_version_touches) ) 577 { 578 tp.operations[0].is_collinear = true; 579 //tp.operations[1].is_collinear = false; 580 tp.method = method_touch; 581 } 582 else 583 { 584 tp.operations[0].is_collinear = true; 585 //tp.operations[1].is_collinear = false; 586 587 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1); 588 base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); 589 590 AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters); 591 } 592 593 tp.operations[0].operation = operation_blocked; 594 tp.operations[1].operation = operation_continue; // boundary 595 *out++ = tp; 596 tp.operations[0].operation = operation_continue; // boundary 597 //tp.operations[1].operation = operation_continue; // boundary 598 *out++ = tp; 599 600 return true; 601 } 602 } 603 /*else if ( is_q_spike ) 604 { 605 tp.operations[0].is_collinear = true; 606 tp.method = is_version_touches ? method_touch : method_touch_interior; 607 tp.operations[0].operation = operation_continue; 608 tp.operations[1].operation = operation_continue; // boundary 609 *out++ = tp; 610 *out++ = tp; 611 612 return true; 613 }*/ 614 615 return false; 616 } 617 replace_method_and_operations_tmboost::geometry::detail::overlay::get_turn_info_linear_areal618 static inline void replace_method_and_operations_tm(method_type & method, 619 operation_type & op0, 620 operation_type & op1) 621 { 622 if ( op0 == operation_blocked && op1 == operation_blocked ) 623 { 624 // NOTE: probably only if methods are WRT IPs, not segments! 625 method = (method == method_touch ? method_equal : method_collinear); 626 } 627 628 // Assuming G1 is always Linear 629 if ( op0 == operation_blocked ) 630 { 631 op0 = operation_continue; 632 } 633 634 if ( op1 == operation_blocked ) 635 { 636 op1 = operation_continue; 637 } 638 else if ( op1 == operation_intersection ) 639 { 640 op1 = operation_union; 641 } 642 643 // spikes in 'm' 644 if ( method == method_error ) 645 { 646 method = method_touch_interior; 647 op0 = operation_union; 648 op1 = operation_union; 649 } 650 } 651 652 template <bool IsFront> 653 class turn_transformer_ec 654 { 655 public: turn_transformer_ec(method_type method_t_or_m)656 explicit turn_transformer_ec(method_type method_t_or_m) 657 : m_method(method_t_or_m) 658 {} 659 660 template <typename Turn> operator ()(Turn & turn) const661 void operator()(Turn & turn) const 662 { 663 operation_type & op0 = turn.operations[0].operation; 664 operation_type & op1 = turn.operations[1].operation; 665 666 // NOTE: probably only if methods are WRT IPs, not segments! 667 if ( BOOST_GEOMETRY_CONDITION(IsFront) 668 || op0 == operation_intersection || op0 == operation_union 669 || op1 == operation_intersection || op1 == operation_union ) 670 { 671 turn.method = m_method; 672 } 673 674 turn.operations[0].is_collinear = op0 != operation_blocked; 675 676 // Assuming G1 is always Linear 677 if ( op0 == operation_blocked ) 678 { 679 op0 = operation_continue; 680 } 681 682 if ( op1 == operation_blocked ) 683 { 684 op1 = operation_continue; 685 } 686 else if ( op1 == operation_intersection ) 687 { 688 op1 = operation_union; 689 } 690 } 691 692 private: 693 method_type m_method; 694 }; 695 replace_operations_iboost::geometry::detail::overlay::get_turn_info_linear_areal696 static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1) 697 { 698 // assuming Linear is always the first one 699 op1 = operation_union; 700 } 701 702 // NOTE: Spikes may NOT be handled for Linear endpoints because it's not 703 // possible to define a spike on an endpoint. Areal geometries must 704 // NOT have spikes at all. One thing that could be done is to throw 705 // an exception when spike is detected in Areal geometry. 706 707 template <bool EnableFirst, 708 bool EnableLast, 709 typename Point1, 710 typename Point2, 711 typename TurnInfo, 712 typename IntersectionInfo, 713 typename OutputIterator> get_turn_info_for_endpointboost::geometry::detail::overlay::get_turn_info_linear_areal714 static inline bool get_turn_info_for_endpoint( 715 Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/, 716 Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/, 717 bool is_p_first, bool is_p_last, 718 bool /*is_q_first*/, bool is_q_last, 719 TurnInfo const& tp_model, 720 IntersectionInfo const& inters, 721 method_type /*method*/, 722 OutputIterator out) 723 { 724 namespace ov = overlay; 725 typedef ov::get_turn_info_for_endpoint<AssignPolicy, EnableFirst, EnableLast> get_info_e; 726 727 const std::size_t ip_count = inters.i_info().count; 728 // no intersection points 729 if ( ip_count == 0 ) 730 return false; 731 732 if ( !is_p_first && !is_p_last ) 733 return false; 734 735 // TODO: is_q_last could probably be replaced by false and removed from parameters 736 737 linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); 738 linear_intersections::ip_info const& ip0 = intersections.template get<0>(); 739 linear_intersections::ip_info const& ip1 = intersections.template get<1>(); 740 741 const bool opposite = inters.d_info().opposite; 742 743 // ANALYSE AND ASSIGN FIRST 744 745 // IP on the first point of Linear Geometry 746 bool was_first_point_handled = false; 747 if ( BOOST_GEOMETRY_CONDITION(EnableFirst) 748 && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication 749 { 750 TurnInfo tp = tp_model; 751 tp.operations[0].position = position_front; 752 tp.operations[1].position = position_middle; 753 754 if ( opposite ) // opposite -> collinear 755 { 756 tp.operations[0].operation = operation_continue; 757 tp.operations[1].operation = operation_union; 758 tp.method = ip0.is_qj ? method_touch : method_touch_interior; 759 } 760 else 761 { 762 typedef typename IntersectionInfo::robust_point1_type rp1_type; 763 typedef typename IntersectionInfo::robust_point2_type rp2_type; 764 765 method_type replaced_method = method_touch_interior; 766 767 if ( ip0.is_qj ) 768 { 769 side_calculator 770 < 771 typename IntersectionInfo::cs_tag, 772 rp1_type, rp2_type, 773 typename IntersectionInfo::side_strategy_type, 774 rp2_type 775 > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), 776 inters.rqi(), inters.rqj(), inters.rqk(), 777 inters.get_side_strategy()); 778 779 std::pair<operation_type, operation_type> 780 operations = get_info_e::operations_of_equal(side_calc); 781 782 tp.operations[0].operation = operations.first; 783 tp.operations[1].operation = operations.second; 784 785 replaced_method = method_touch; 786 } 787 else 788 { 789 side_calculator 790 < 791 typename IntersectionInfo::cs_tag, 792 rp1_type, rp2_type, 793 typename IntersectionInfo::side_strategy_type, 794 rp2_type, rp1_type, rp1_type, 795 rp2_type, rp1_type, rp2_type 796 > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), 797 inters.rqi(), inters.rpi(), inters.rqj(), 798 inters.get_side_strategy()); 799 800 std::pair<operation_type, operation_type> 801 operations = get_info_e::operations_of_equal(side_calc); 802 803 tp.operations[0].operation = operations.first; 804 tp.operations[1].operation = operations.second; 805 } 806 807 turn_transformer_ec<true> transformer(replaced_method); 808 transformer(tp); 809 } 810 811 // equals<> or collinear<> will assign the second point, 812 // we'd like to assign the first one 813 base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0); 814 815 // NOTE: is_collinear is not set for the first endpoint of L 816 // for which there is no preceding segment 817 // here is_p_first_ip == true 818 tp.operations[0].is_collinear = false; 819 820 AssignPolicy::apply(tp, pi, qi, inters); 821 *out++ = tp; 822 823 was_first_point_handled = true; 824 } 825 826 // ANALYSE AND ASSIGN LAST 827 828 // IP on the last point of Linear Geometry 829 if ( BOOST_GEOMETRY_CONDITION(EnableLast) 830 && is_p_last 831 && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication 832 { 833 TurnInfo tp = tp_model; 834 835 if ( inters.i_info().count > 1 ) 836 { 837 //BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); 838 tp.operations[0].is_collinear = true; 839 tp.operations[1].operation = opposite ? operation_continue : operation_union; 840 } 841 else //if ( result.template get<0>().count == 1 ) 842 { 843 side_calculator 844 < 845 typename IntersectionInfo::cs_tag, 846 typename IntersectionInfo::robust_point1_type, 847 typename IntersectionInfo::robust_point2_type, 848 typename IntersectionInfo::side_strategy_type, 849 typename IntersectionInfo::robust_point2_type 850 > side_calc(inters.rqi(), inters.rpj(), inters.rpi(), 851 inters.rqi(), inters.rqj(), inters.rqk(), 852 inters.get_side_strategy()); 853 854 std::pair<operation_type, operation_type> 855 operations = get_info_e::operations_of_equal(side_calc); 856 857 tp.operations[0].operation = operations.first; 858 tp.operations[1].operation = operations.second; 859 860 turn_transformer_ec<false> transformer(method_none); 861 transformer(tp); 862 863 tp.operations[0].is_collinear = tp.both(operation_continue); 864 } 865 866 tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior; 867 tp.operations[0].operation = operation_blocked; 868 tp.operations[0].position = position_back; 869 tp.operations[1].position = position_middle; 870 871 // equals<> or collinear<> will assign the second point, 872 // we'd like to assign the first one 873 unsigned int ip_index = ip_count > 1 ? 1 : 0; 874 base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index); 875 876 AssignPolicy::apply(tp, pi, qi, inters); 877 *out++ = tp; 878 879 // don't ignore the first IP if the segment is opposite 880 return !( opposite && ip_count > 1 ) || was_first_point_handled; 881 } 882 883 // don't ignore anything for now 884 return false; 885 } 886 }; 887 888 }} // namespace detail::overlay 889 #endif // DOXYGEN_NO_DETAIL 890 891 }} // namespace boost::geometry 892 893 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP 894