Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:35:10

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
0004 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
0005 
0006 // This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
0007 // Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
0008 
0009 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
0010 
0011 // Use, modification and distribution is subject to the Boost Software License,
0012 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0013 // http://www.boost.org/LICENSE_1_0.txt)
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/condition.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 RobustPolicy,
0044         typename OutputIterator
0045     >
0046     static inline OutputIterator apply(
0047                 UniqueSubRange1 const& range_p,
0048                 UniqueSubRange2 const& range_q,
0049                 TurnInfo const& tp_model,
0050                 UmbrellaStrategy const& umbrella_strategy,
0051                 RobustPolicy const& robust_policy,
0052                 OutputIterator out)
0053     {
0054         typedef intersection_info
0055             <
0056                 UniqueSubRange1, UniqueSubRange2,
0057                 typename TurnInfo::point_type,
0058                 UmbrellaStrategy,
0059                 RobustPolicy
0060             > inters_info;
0061 
0062         inters_info inters(range_p, range_q, umbrella_strategy, robust_policy);
0063 
0064         char const method = inters.d_info().how;
0065 
0066         // Copy, to copy possibly extended fields
0067         TurnInfo tp = tp_model;
0068 
0069         // Select method and apply
0070         switch(method)
0071         {
0072             case 'a' : // collinear, "at"
0073             case 'f' : // collinear, "from"
0074             case 's' : // starts from the middle
0075                 get_turn_info_for_endpoint<true, true>
0076                     ::apply(range_p, range_q,
0077                             tp_model, inters, method_none, out,
0078                             umbrella_strategy);
0079                 break;
0080 
0081             case 'd' : // disjoint: never do anything
0082                 break;
0083 
0084             case 'm' :
0085             {
0086                 using handler = touch_interior<TurnInfo, verify_policy_ll>;
0087 
0088                 if ( get_turn_info_for_endpoint<false, true>
0089                         ::apply(range_p, range_q,
0090                                 tp_model, inters, method_touch_interior, out,
0091                                 umbrella_strategy) )
0092                 {
0093                     // do nothing
0094                 }
0095                 else
0096                 {
0097                     // If Q (1) arrives (1)
0098                     if ( inters.d_info().arrival[1] == 1)
0099                     {
0100                         handler::template apply<0>(range_p, range_q, tp,
0101                                                   inters.i_info(), inters.d_info(),
0102                                                   inters.sides(),
0103                                                   umbrella_strategy);
0104                     }
0105                     else
0106                     {
0107                         // Swap p/q
0108                         handler::template apply<1>(range_q, range_p, tp,
0109                                                   inters.i_info(), inters.d_info(),
0110                                                   inters.swapped_sides(),
0111                                                   umbrella_strategy);
0112                     }
0113 
0114                     if ( tp.operations[0].operation == operation_blocked )
0115                     {
0116                         tp.operations[1].is_collinear = true;
0117                     }
0118                     if ( tp.operations[1].operation == operation_blocked )
0119                     {
0120                         tp.operations[0].is_collinear = true;
0121                     }
0122 
0123                     replace_method_and_operations_tm(tp.method,
0124                                                      tp.operations[0].operation,
0125                                                      tp.operations[1].operation);
0126 
0127                     *out++ = tp;
0128                 }
0129             }
0130             break;
0131             case 'i' :
0132             {
0133                 crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
0134 
0135                 replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
0136 
0137                 *out++ = tp;
0138             }
0139             break;
0140             case 't' :
0141             {
0142                 using handler = touch<TurnInfo, verify_policy_ll>;
0143 
0144                 // Both touch (both arrive there)
0145                 if ( get_turn_info_for_endpoint<false, true>
0146                         ::apply(range_p, range_q,
0147                                 tp_model, inters, method_touch, out,
0148                                 umbrella_strategy) )
0149                 {
0150                     // do nothing
0151                 }
0152                 else
0153                 {
0154                     handler::apply(range_p, range_q, tp,
0155                                    inters.i_info(), inters.d_info(),
0156                                    inters.sides(),
0157                                    umbrella_strategy);
0158 
0159                     // workarounds for touch<> not taking spikes into account starts here
0160                     // those was discovered empirically
0161                     // touch<> is not symmetrical!
0162                     // P spikes and Q spikes may produce various operations!
0163                     // TODO: this is not optimal solution - think about rewriting touch<>
0164 
0165                     if ( tp.operations[0].operation == operation_blocked
0166                       && tp.operations[1].operation == operation_blocked )
0167                     {
0168                         // two touching spikes on the same line
0169                         if ( inters.is_spike_p() && inters.is_spike_q() )
0170                         {
0171                             tp.operations[0].operation = operation_union;
0172                             tp.operations[1].operation = operation_union;
0173                         }
0174                         else
0175                         {
0176                             tp.operations[0].is_collinear = true;
0177                             tp.operations[1].is_collinear = true;
0178                         }
0179                     }
0180                     else if ( tp.operations[0].operation == operation_blocked )
0181                     {
0182                         // a spike on P on the same line with Q1
0183                         if ( inters.is_spike_p() )
0184                         {
0185                             if ( inters.sides().qk_wrt_p1() == 0 )
0186                             {
0187                                 tp.operations[0].is_collinear = true;
0188                             }
0189                             else
0190                             {
0191                                 tp.operations[0].operation = operation_union;
0192                             }
0193                         }
0194                         else
0195                         {
0196                             tp.operations[1].is_collinear = true;
0197                         }
0198                     }
0199                     else if ( tp.operations[1].operation == operation_blocked )
0200                     {
0201                         // a spike on Q on the same line with P1
0202                         if ( inters.is_spike_q() )
0203                         {
0204                             if ( inters.sides().pk_wrt_q1() == 0 )
0205                             {
0206                                 tp.operations[1].is_collinear = true;
0207                             }
0208                             else
0209                             {
0210                                 tp.operations[1].operation = operation_union;
0211                             }
0212                         }
0213                         else
0214                         {
0215                             tp.operations[0].is_collinear = true;
0216                         }
0217                     }
0218                     else if ( tp.operations[0].operation == operation_continue
0219                            && tp.operations[1].operation == operation_continue )
0220                     {
0221                         // P spike on the same line with Q2 (opposite)
0222                         if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
0223                           && inters.is_spike_p() )
0224                         {
0225                             tp.operations[0].operation = operation_union;
0226                             tp.operations[1].operation = operation_union;
0227                         }
0228                     }
0229                     else if ( tp.operations[0].operation == operation_none
0230                            && tp.operations[1].operation == operation_none )
0231                     {
0232                         // spike not handled by touch<>
0233                         bool const is_p = inters.is_spike_p();
0234                         bool const is_q = inters.is_spike_q();
0235 
0236                         if ( is_p || is_q )
0237                         {
0238                             tp.operations[0].operation = operation_union;
0239                             tp.operations[1].operation = operation_union;
0240 
0241                             if ( inters.sides().pk_wrt_q2() == 0 )
0242                             {
0243                                 tp.operations[0].operation = operation_continue; // will be converted to i
0244                                 if ( is_p )
0245                                 {
0246                                     tp.operations[0].is_collinear = true;
0247                                 }
0248                             }
0249 
0250                             if ( inters.sides().qk_wrt_p2() == 0 )
0251                             {
0252                                 tp.operations[1].operation = operation_continue; // will be converted to i
0253                                 if ( is_q )
0254                                 {
0255                                     tp.operations[1].is_collinear = true;
0256                                 }
0257                             }
0258                         }
0259                     }
0260 
0261                     // workarounds for touch<> not taking spikes into account ends here
0262 
0263                     replace_method_and_operations_tm(tp.method,
0264                                                      tp.operations[0].operation,
0265                                                      tp.operations[1].operation);
0266 
0267                     if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
0268                       || ! 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                     // do nothing
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                         // Both equal
0294                         // or collinear-and-ending at intersection point
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                         // transform turn
0306                         turn_transformer_ec transformer(method_touch);
0307                         transformer(tp);
0308 
0309                         // conditionally handle spikes
0310                         if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
0311                           || ! append_collinear_spikes(tp, inters,
0312                                                        method_touch, spike_op,
0313                                                        out) )
0314                         {
0315                             *out++ = tp; // no spikes
0316                         }
0317                     }
0318                     else
0319                     {
0320                         // TODO: ignore for spikes or generate something else than opposite?
0321 
0322                         equal_opposite
0323                             <
0324                                 TurnInfo,
0325                                 AssignPolicy
0326                             >::apply(range_p, range_q, tp, out, inters);
0327                     }
0328                 }
0329             }
0330             break;
0331             case 'c' :
0332             {
0333                 // Collinear
0334                 if ( get_turn_info_for_endpoint<true, true>
0335                         ::apply(range_p, range_q,
0336                                 tp_model, inters,  method_collinear, out,
0337                                 umbrella_strategy) )
0338                 {
0339                     // do nothing
0340                 }
0341                 else
0342                 {
0343                     // NOTE: this is for spikes since those are set in the turn_transformer_ec
0344                     tp.operations[0].is_collinear = true;
0345                     tp.operations[1].is_collinear = true;
0346 
0347                     if ( ! inters.d_info().opposite )
0348                     {
0349                         method_type method_replace = method_touch_interior;
0350                         operation_type spike_op = operation_continue;
0351 
0352                         if ( inters.d_info().arrival[0] == 0 )
0353                         {
0354                             // Collinear, but similar thus handled as equal
0355                             using handler = equal<TurnInfo, verify_policy_ll>;
0356 
0357                             handler::apply(range_p, range_q, tp,
0358                                 inters.i_info(), inters.d_info(), inters.sides(),
0359                                 umbrella_strategy);
0360 
0361                             method_replace = method_touch;
0362                             if ( tp.operations[0].operation != operation_continue
0363                               || tp.operations[1].operation != operation_continue )
0364                             {
0365                                 spike_op = operation_union;
0366                             }
0367                         }
0368                         else
0369                         {
0370                             using handler = collinear<TurnInfo, verify_policy_ll>;
0371                             handler::apply(range_p, range_q, tp,
0372                                            inters.i_info(), inters.d_info(),
0373                                            inters.sides());
0374 
0375                             //method_replace = method_touch_interior;
0376                             //spike_op = operation_continue;
0377                         }
0378 
0379                         // transform turn
0380                         turn_transformer_ec transformer(method_replace);
0381                         transformer(tp);
0382 
0383                         // conditionally handle spikes
0384                         if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
0385                           || ! append_collinear_spikes(tp, inters,
0386                                                        method_replace, spike_op,
0387                                                        out) )
0388                         {
0389                             // no spikes
0390                             *out++ = tp;
0391                         }
0392                     }
0393                     else
0394                     {
0395                         // If this always 'm' ?
0396                         turn_transformer_ec transformer(method_touch_interior);
0397 
0398                         // conditionally handle spikes
0399                         if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
0400                         {
0401                             append_opposite_spikes<append_collinear_opposite>(tp, inters, out);
0402                         }
0403 
0404                         // TODO: ignore for spikes?
0405                         //       E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
0406                         //       the same with is_q_valid
0407 
0408                         collinear_opposite
0409                             <
0410                                 TurnInfo,
0411                                 AssignPolicy
0412                             >::apply(range_p, range_q,
0413                                 tp, out, inters, inters.sides(),
0414                                 transformer);
0415                     }
0416                 }
0417             }
0418             break;
0419             case '0' :
0420             {
0421                 // degenerate points
0422                 if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
0423                 {
0424                     only_convert::apply(tp, inters.i_info());
0425 
0426                     // if any, only one of those should be true
0427                     if ( range_p.is_first_segment()
0428                       && equals::equals_point_point(range_p.at(0), tp.point, umbrella_strategy) )
0429                     {
0430                         tp.operations[0].position = position_front;
0431                     }
0432                     else if ( range_p.is_last_segment()
0433                            && equals::equals_point_point(range_p.at(1), tp.point, umbrella_strategy) )
0434                     {
0435                         tp.operations[0].position = position_back;
0436                     }
0437                     else if ( range_q.is_first_segment()
0438                            && equals::equals_point_point(range_q.at(0), tp.point, umbrella_strategy) )
0439                     {
0440                         tp.operations[1].position = position_front;
0441                     }
0442                     else if ( range_q.is_last_segment()
0443                            && equals::equals_point_point(range_q.at(1), tp.point, umbrella_strategy) )
0444                     {
0445                         tp.operations[1].position = position_back;
0446                     }
0447 
0448                     *out++ = tp;
0449                 }
0450             }
0451             break;
0452             default :
0453             {
0454 #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
0455                 std::cout << "TURN: Unknown method: " << method << std::endl;
0456 #endif
0457 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
0458                 BOOST_THROW_EXCEPTION(turn_info_exception(method));
0459 #endif
0460             }
0461             break;
0462         }
0463 
0464         return out;
0465     }
0466 
0467     template <typename TurnInfo,
0468               typename IntersectionInfo,
0469               typename OutIt>
0470     static inline bool append_collinear_spikes(TurnInfo & tp,
0471                                                IntersectionInfo const& inters_info,
0472                                                method_type method, operation_type spike_op,
0473                                                OutIt out)
0474     {
0475         // method == touch || touch_interior
0476         // both position == middle
0477 
0478         bool is_p_spike = tp.operations[0].operation == spike_op
0479                        && inters_info.is_spike_p();
0480         bool is_q_spike = tp.operations[1].operation == spike_op
0481                        && inters_info.is_spike_q();
0482 
0483         if ( is_p_spike && is_q_spike )
0484         {
0485             if ( tp.method == method_equal
0486               && tp.operations[0].operation == operation_continue
0487               && tp.operations[1].operation == operation_continue )
0488             {
0489                 // treat both non-opposite collinear spikes as no-spikes
0490                 return false;
0491             }
0492 
0493             tp.method = method;
0494             tp.operations[0].operation = operation_blocked;
0495             tp.operations[1].operation = operation_blocked;
0496             *out++ = tp;
0497             tp.operations[0].operation = operation_intersection;
0498             tp.operations[1].operation = operation_intersection;
0499             *out++ = tp;
0500 
0501             return true;
0502         }
0503         else if ( is_p_spike )
0504         {
0505             tp.method = method;
0506             tp.operations[0].operation = operation_blocked;
0507             tp.operations[1].operation = operation_union;
0508             *out++ = tp;
0509             tp.operations[0].operation = operation_intersection;
0510             //tp.operations[1].operation = operation_union;
0511             *out++ = tp;
0512 
0513             return true;
0514         }
0515         else if ( is_q_spike )
0516         {
0517             tp.method = method;
0518             tp.operations[0].operation = operation_union;
0519             tp.operations[1].operation = operation_blocked;
0520             *out++ = tp;
0521             //tp.operations[0].operation = operation_union;
0522             tp.operations[1].operation = operation_intersection;
0523             *out++ = tp;
0524 
0525             return true;
0526         }
0527 
0528         return false;
0529     }
0530 
0531     enum append_version { append_touches, append_collinear_opposite };
0532 
0533     template <append_version Version,
0534               typename TurnInfo,
0535               typename IntersectionInfo,
0536               typename OutIt>
0537     static inline bool append_opposite_spikes(TurnInfo & tp,
0538                                               IntersectionInfo const& inters,
0539                                               OutIt out)
0540     {
0541         static const bool is_version_touches = (Version == append_touches);
0542 
0543         bool is_p_spike = ( is_version_touches ?
0544                             ( tp.operations[0].operation == operation_continue
0545                            || tp.operations[0].operation == operation_intersection ) :
0546                             true )
0547                        && inters.is_spike_p();
0548         bool is_q_spike = ( is_version_touches ?
0549                             ( tp.operations[1].operation == operation_continue
0550                            || tp.operations[1].operation == operation_intersection ) :
0551                             true )
0552                        && inters.is_spike_q();
0553 
0554         bool res = false;
0555 
0556         if ( is_p_spike
0557           && ( BOOST_GEOMETRY_CONDITION(is_version_touches)
0558             || inters.d_info().arrival[0] == 1 ) )
0559         {
0560             if ( BOOST_GEOMETRY_CONDITION(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             else // Version == append_collinear_opposite
0567             {
0568                 tp.operations[0].is_collinear = true;
0569                 tp.operations[1].is_collinear = false;
0570 
0571                 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
0572 
0573                 base_turn_handler::assign_point(tp, method_touch_interior,
0574                                                 inters.i_info(), 1);
0575             }
0576 
0577             tp.operations[0].operation = operation_blocked;
0578             tp.operations[1].operation = operation_intersection;
0579             *out++ = tp;
0580             tp.operations[0].operation = operation_intersection;
0581             //tp.operations[1].operation = operation_intersection;
0582             *out++ = tp;
0583 
0584             res = true;
0585         }
0586 
0587         if ( is_q_spike
0588           && ( BOOST_GEOMETRY_CONDITION(is_version_touches)
0589             || inters.d_info().arrival[1] == 1 ) )
0590         {
0591             if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
0592             {
0593                 tp.operations[0].is_collinear = false;
0594                 tp.operations[1].is_collinear = true;
0595                 tp.method = method_touch;
0596             }
0597             else // Version == append_collinear_opposite
0598             {
0599                 tp.operations[0].is_collinear = false;
0600                 tp.operations[1].is_collinear = true;
0601 
0602                 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 0);
0603 
0604                 base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0);
0605             }
0606 
0607             tp.operations[0].operation = operation_intersection;
0608             tp.operations[1].operation = operation_blocked;
0609             *out++ = tp;
0610             //tp.operations[0].operation = operation_intersection;
0611             tp.operations[1].operation = operation_intersection;
0612             *out++ = tp;
0613 
0614             res = true;
0615         }
0616 
0617         return res;
0618     }
0619 
0620     static inline void replace_method_and_operations_tm(method_type & method,
0621                                                         operation_type & op0,
0622                                                         operation_type & op1)
0623     {
0624         if ( op0 == operation_blocked && op1 == operation_blocked )
0625         {
0626             // NOTE: probably only if methods are WRT IPs, not segments!
0627             method = (method == method_touch ? method_equal : method_collinear);
0628             op0 = operation_continue;
0629             op1 = operation_continue;
0630         }
0631         else
0632         {
0633             if ( op0 == operation_continue || op0 == operation_blocked )
0634             {
0635                 op0 = operation_intersection;
0636             }
0637             else if ( op0 == operation_intersection )
0638             {
0639                 op0 = operation_union;
0640             }
0641 
0642             if ( op1 == operation_continue || op1 == operation_blocked )
0643             {
0644                 op1 = operation_intersection;
0645             }
0646             else if ( op1 == operation_intersection )
0647             {
0648                 op1 = operation_union;
0649             }
0650 
0651             // spikes in 'm'
0652             if ( method == method_error )
0653             {
0654                 method = method_touch_interior;
0655                 op0 = operation_union;
0656                 op1 = operation_union;
0657             }
0658         }
0659     }
0660 
0661     class turn_transformer_ec
0662     {
0663     public:
0664         explicit turn_transformer_ec(method_type method_t_or_m)
0665             : m_method(method_t_or_m)
0666         {}
0667 
0668         template <typename Turn>
0669         void operator()(Turn & turn) const
0670         {
0671             operation_type & op0 = turn.operations[0].operation;
0672             operation_type & op1 = turn.operations[1].operation;
0673 
0674             BOOST_GEOMETRY_ASSERT(op0 != operation_blocked || op1 != operation_blocked );
0675 
0676             if ( op0 == operation_blocked )
0677             {
0678                 op0 = operation_intersection;
0679             }
0680             else if ( op0 == operation_intersection )
0681             {
0682                 op0 = operation_union;
0683             }
0684 
0685             if ( op1 == operation_blocked )
0686             {
0687                 op1 = operation_intersection;
0688             }
0689             else if ( op1 == operation_intersection )
0690             {
0691                 op1 = operation_union;
0692             }
0693 
0694             if ( op0 == operation_intersection || op0 == operation_union
0695               || op1 == operation_intersection || op1 == operation_union )
0696             {
0697                 turn.method = m_method;
0698             }
0699 
0700 // TODO: is this correct?
0701 //       it's equivalent to comparing to operation_blocked at the beginning of the function
0702             turn.operations[0].is_collinear = op0 != operation_intersection;
0703             turn.operations[1].is_collinear = op1 != operation_intersection;
0704         }
0705 
0706     private:
0707         method_type m_method;
0708     };
0709 
0710     static inline void replace_operations_i(operation_type & op0, operation_type & op1)
0711     {
0712         if ( op0 == operation_intersection )
0713         {
0714             op0 = operation_union;
0715         }
0716 
0717         if ( op1 == operation_intersection )
0718         {
0719             op1 = operation_union;
0720         }
0721     }
0722 };
0723 
0724 }} // namespace detail::overlay
0725 #endif // DOXYGEN_NO_DETAIL
0726 
0727 }} // namespace boost::geometry
0728 
0729 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP