Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:30:36

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
0004 // Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
0005 
0006 // This file was modified by Oracle on 2013-2024.
0007 // Modifications copyright (c) 2013-2024 Oracle and/or its affiliates.
0008 // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
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/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         // Copy, to copy possibly extended fields
0064         TurnInfo tp = tp_model;
0065 
0066         // Select method and apply
0067         switch(method)
0068         {
0069             case 'a' : // collinear, "at"
0070             case 'f' : // collinear, "from"
0071             case 's' : // starts from the middle
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' : // disjoint: never do anything
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                     // do nothing
0091                 }
0092                 else
0093                 {
0094                     // If Q (1) arrives (1)
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                         // Swap p/q
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                 // Both touch (both arrive there)
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                     // do nothing
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                     // workarounds for touch<> not taking spikes into account starts here
0157                     // those was discovered empirically
0158                     // touch<> is not symmetrical!
0159                     // P spikes and Q spikes may produce various operations!
0160                     // TODO: this is not optimal solution - think about rewriting touch<>
0161 
0162                     if ( tp.operations[0].operation == operation_blocked
0163                       && tp.operations[1].operation == operation_blocked )
0164                     {
0165                         // two touching spikes on the same line
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                         // a spike on P on the same line with Q1
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                         // a spike on Q on the same line with P1
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                         // P spike on the same line with Q2 (opposite)
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                         // spike not handled by touch<>
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; // will be converted to i
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; // will be converted to i
0250                                 if ( is_q )
0251                                 {
0252                                     tp.operations[1].is_collinear = true;
0253                                 }
0254                             }
0255                         }
0256                     }
0257 
0258                     // workarounds for touch<> not taking spikes into account ends here
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                     // 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_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; // no spikes
0318                         }
0319                     }
0320                     else
0321                     {
0322                         // TODO: ignore for spikes or generate something else than opposite?
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                 // Collinear
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                     // do nothing
0342                 }
0343                 else
0344                 {
0345                     // NOTE: this is for spikes since those are set in the turn_transformer_ec
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                             // Collinear, but similar thus handled as equal
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                             //method_replace = method_touch_interior;
0378                             //spike_op = operation_continue;
0379                         }
0380 
0381                         // transform turn
0382                         turn_transformer_ec transformer(method_replace);
0383                         transformer(tp);
0384 
0385                         // conditionally handle spikes
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                             // no spikes
0394                             *out++ = tp;
0395                         }
0396                     }
0397                     else
0398                     {
0399                         // If this always 'm' ?
0400                         turn_transformer_ec transformer(method_touch_interior);
0401 
0402                         // conditionally handle spikes
0403                         if BOOST_GEOMETRY_CONSTEXPR (handle_spikes)
0404                         {
0405                             append_opposite_spikes<append_collinear_opposite>(tp, inters, out);
0406                         }
0407 
0408                         // TODO: ignore for spikes?
0409                         //       E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
0410                         //       the same with is_q_valid
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                 // degenerate points
0426                 if BOOST_GEOMETRY_CONSTEXPR (AssignPolicy::include_degenerate)
0427                 {
0428                     only_convert::apply(tp, inters.i_info());
0429 
0430                     // if any, only one of those should be true
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         // method == touch || touch_interior
0477         // both position == middle
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                 // treat both non-opposite collinear spikes as no-spikes
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             //tp.operations[1].operation = operation_union;
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             //tp.operations[0].operation = operation_union;
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) // Version == append_collinear_opposite
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                 //tp.operations[1].operation = operation_intersection;
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) // Version == append_collinear_opposite
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                 //tp.operations[0].operation = operation_intersection;
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             // NOTE: probably only if methods are WRT IPs, not segments!
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             // spikes in 'm'
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 // TODO: is this correct?
0712 //       it's equivalent to comparing to operation_blocked at the beginning of the function
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 }} // namespace detail::overlay
0736 #endif // DOXYGEN_NO_DETAIL
0737 
0738 }} // namespace boost::geometry
0739 
0740 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP