Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
0004 
0005 // This file was modified by Oracle on 2013-2022.
0006 // Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
0007 
0008 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
0009 
0010 // Use, modification and distribution is subject to the Boost Software License,
0011 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0012 // http://www.boost.org/LICENSE_1_0.txt)
0013 
0014 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP
0015 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP
0016 
0017 #include <algorithm>
0018 
0019 #include <boost/core/ignore_unused.hpp>
0020 #include <boost/range/size.hpp>
0021 
0022 #include <boost/geometry/core/assert.hpp>
0023 
0024 #include <boost/geometry/util/condition.hpp>
0025 #include <boost/geometry/util/range.hpp>
0026 
0027 #include <boost/geometry/algorithms/detail/sub_range.hpp>
0028 #include <boost/geometry/algorithms/detail/single_geometry.hpp>
0029 
0030 #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
0031 #include <boost/geometry/algorithms/detail/relate/result.hpp>
0032 #include <boost/geometry/algorithms/detail/relate/turns.hpp>
0033 #include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
0034 #include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
0035 
0036 #include <boost/geometry/geometries/helper_geometry.hpp>
0037 
0038 namespace boost { namespace geometry
0039 {
0040 
0041 #ifndef DOXYGEN_NO_DETAIL
0042 namespace detail { namespace relate {
0043 
0044 template <typename Result, typename BoundaryChecker, bool TransposeResult>
0045 class disjoint_linestring_pred
0046 {
0047 public:
0048     disjoint_linestring_pred(Result & res,
0049                              BoundaryChecker const& boundary_checker)
0050         : m_result(res)
0051         , m_boundary_checker(boundary_checker)
0052         , m_flags(0)
0053     {
0054         if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
0055         {
0056             m_flags |= 1;
0057         }
0058         if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
0059         {
0060             m_flags |= 2;
0061         }
0062     }
0063 
0064     template <typename Linestring>
0065     bool operator()(Linestring const& linestring)
0066     {
0067         if ( m_flags == 3 )
0068         {
0069             return false;
0070         }
0071 
0072         std::size_t const count = boost::size(linestring);
0073 
0074         // invalid input
0075         if ( count < 2 )
0076         {
0077             // ignore
0078             // TODO: throw an exception?
0079             return true;
0080         }
0081 
0082         // point-like linestring
0083         if ( count == 2
0084           && equals::equals_point_point(range::front(linestring),
0085                                         range::back(linestring),
0086                                         m_boundary_checker.strategy()) )
0087         {
0088             update<interior, exterior, '0', TransposeResult>(m_result);
0089         }
0090         else
0091         {
0092             update<interior, exterior, '1', TransposeResult>(m_result);
0093             m_flags |= 1;
0094 
0095             // check if there is a boundary
0096             if (m_flags < 2
0097                 && (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
0098                     || m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
0099             {
0100                 update<boundary, exterior, '0', TransposeResult>(m_result);
0101                 m_flags |= 2;
0102             }
0103         }
0104 
0105         return m_flags != 3
0106             && ! m_result.interrupt;
0107     }
0108 
0109 private:
0110     Result & m_result;
0111     BoundaryChecker const& m_boundary_checker;
0112     unsigned m_flags;
0113 };
0114 
0115 template <typename Geometry1, typename Geometry2>
0116 struct linear_linear
0117 {
0118     static const bool interruption_enabled = true;
0119 
0120     template <typename Result, typename Strategy>
0121     static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
0122                              Result & result,
0123                              Strategy const& strategy)
0124     {
0125         boundary_checker<Geometry1, Strategy> boundary_checker1(geometry1, strategy);
0126         boundary_checker<Geometry2, Strategy> boundary_checker2(geometry2, strategy);
0127         apply(geometry1, geometry2, boundary_checker1, boundary_checker2, result, strategy);
0128     }
0129 
0130     template <typename BoundaryChecker1, typename BoundaryChecker2, typename Result, typename Strategy>
0131     static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
0132                              BoundaryChecker1 const& boundary_checker1,
0133                              BoundaryChecker2 const& boundary_checker2,
0134                              Result & result,
0135                              Strategy const& strategy)
0136     {
0137         // The result should be FFFFFFFFF
0138         update<exterior, exterior, result_dimension<Geometry1>::value>(result);// FFFFFFFFd, d in [1,9] or T
0139         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0140             return;
0141 
0142         // get and analyse turns
0143         using turn_type = typename turns::get_turns
0144             <
0145                 Geometry1, Geometry2
0146             >::template turn_info_type<Strategy>::type;
0147         std::vector<turn_type> turns;
0148 
0149         interrupt_policy_linear_linear<Result> interrupt_policy(result);
0150 
0151         turns::get_turns
0152             <
0153                 Geometry1,
0154                 Geometry2,
0155                 detail::get_turns::get_turn_info_type<Geometry1, Geometry2, turns::assign_policy<true> >
0156             >::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
0157 
0158         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0159             return;
0160 
0161         disjoint_linestring_pred<Result, BoundaryChecker1, false> pred1(result, boundary_checker1);
0162         for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
0163         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0164             return;
0165 
0166         disjoint_linestring_pred<Result, BoundaryChecker2, true> pred2(result, boundary_checker2);
0167         for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
0168         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0169             return;
0170 
0171         if ( turns.empty() )
0172             return;
0173 
0174         // TODO: turns must be sorted and followed only if it's possible to go out and in on the same point
0175         // for linear geometries union operation must be detected which I guess would be quite often
0176 
0177         if ( may_update<interior, interior, '1'>(result)
0178           || may_update<interior, boundary, '0'>(result)
0179           || may_update<interior, exterior, '1'>(result)
0180           || may_update<boundary, interior, '0'>(result)
0181           || may_update<boundary, boundary, '0'>(result)
0182           || may_update<boundary, exterior, '0'>(result) )
0183         {
0184             using less_t = turns::less<0, turns::less_op_linear_linear<0>, Strategy>;
0185             std::sort(turns.begin(), turns.end(), less_t());
0186 
0187             turns_analyser<turn_type, 0> analyser;
0188             analyse_each_turn(result, analyser,
0189                               turns.begin(), turns.end(),
0190                               geometry1, geometry2,
0191                               boundary_checker1, boundary_checker2);
0192         }
0193 
0194         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0195             return;
0196 
0197         if ( may_update<interior, interior, '1', true>(result)
0198           || may_update<interior, boundary, '0', true>(result)
0199           || may_update<interior, exterior, '1', true>(result)
0200           || may_update<boundary, interior, '0', true>(result)
0201           || may_update<boundary, boundary, '0', true>(result)
0202           || may_update<boundary, exterior, '0', true>(result) )
0203         {
0204             using less_t = turns::less<1, turns::less_op_linear_linear<1>, Strategy>;
0205             std::sort(turns.begin(), turns.end(), less_t());
0206 
0207             turns_analyser<turn_type, 1> analyser;
0208             analyse_each_turn(result, analyser,
0209                               turns.begin(), turns.end(),
0210                               geometry2, geometry1,
0211                               boundary_checker2, boundary_checker1);
0212         }
0213     }
0214 
0215     template <typename Result>
0216     class interrupt_policy_linear_linear
0217     {
0218     public:
0219         static bool const enabled = true;
0220 
0221         explicit interrupt_policy_linear_linear(Result & result)
0222             : m_result(result)
0223         {}
0224 
0225 // TODO: since we update result for some operations here, we may not do it in the analyser!
0226 
0227         template <typename Range>
0228         inline bool apply(Range const& turns)
0229         {
0230             for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
0231             {
0232                 if ( it->operations[0].operation == overlay::operation_intersection
0233                   || it->operations[1].operation == overlay::operation_intersection )
0234                 {
0235                     update<interior, interior, '1'>(m_result);
0236                 }
0237                 else if ( ( it->operations[0].operation == overlay::operation_union
0238                          || it->operations[0].operation == overlay::operation_blocked
0239                          || it->operations[1].operation == overlay::operation_union
0240                          || it->operations[1].operation == overlay::operation_blocked )
0241                        && it->operations[0].position == overlay::position_middle
0242                        && it->operations[1].position == overlay::position_middle )
0243                 {
0244 // TODO: here we could also check the boundaries and set IB,BI,BB at this point
0245                     update<interior, interior, '0'>(m_result);
0246                 }
0247             }
0248 
0249             return m_result.interrupt;
0250         }
0251 
0252     private:
0253         Result & m_result;
0254     };
0255 
0256     // This analyser should be used like Input or SinglePass Iterator
0257     template <typename TurnInfo, std::size_t OpId>
0258     class turns_analyser
0259     {
0260         typedef typename TurnInfo::point_type turn_point_type;
0261 
0262         static const std::size_t op_id = OpId;
0263         static const std::size_t other_op_id = (OpId + 1) % 2;
0264         static const bool transpose_result = OpId != 0;
0265 
0266     public:
0267         turns_analyser()
0268             : m_previous_turn_ptr(NULL)
0269             , m_previous_operation(overlay::operation_none)
0270             , m_degenerated_turn_ptr(NULL)
0271             , m_collinear_spike_exit(false)
0272         {}
0273 
0274         template <typename Result,
0275                   typename TurnIt,
0276                   typename Geometry,
0277                   typename OtherGeometry,
0278                   typename BoundaryChecker,
0279                   typename OtherBoundaryChecker>
0280         void apply(Result & res, TurnIt it,
0281                    Geometry const& geometry,
0282                    OtherGeometry const& other_geometry,
0283                    BoundaryChecker const& boundary_checker,
0284                    OtherBoundaryChecker const& other_boundary_checker)
0285         {
0286             overlay::operation_type const op = it->operations[op_id].operation;
0287 
0288             segment_identifier const& seg_id = it->operations[op_id].seg_id;
0289 
0290             bool const first_in_range = m_seg_watcher.update(seg_id);
0291 
0292             if ( op != overlay::operation_union
0293               && op != overlay::operation_intersection
0294               && op != overlay::operation_blocked )
0295             {
0296                 // degenerated turn
0297                 if ( op == overlay::operation_continue
0298                   && it->method == overlay::method_none
0299                   && m_exit_watcher.is_outside(*it)
0300                   /*&& ( m_exit_watcher.get_exit_operation() == overlay::operation_none
0301                     || ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it) )*/ )
0302                 {
0303                     // TODO: rewrite the above condition
0304 
0305                     // WARNING! For spikes the above condition may be TRUE
0306                     // When degenerated turns are be marked in a different way than c,c/c
0307                     // different condition will be checked
0308 
0309                     handle_degenerated(res, *it,
0310                                        geometry, other_geometry,
0311                                        boundary_checker, other_boundary_checker,
0312                                        first_in_range);
0313 
0314                     // TODO: not elegant solution! should be rewritten.
0315                     if ( it->operations[op_id].position == overlay::position_back )
0316                     {
0317                         m_previous_operation = overlay::operation_blocked;
0318                         m_exit_watcher.reset_detected_exit();
0319                     }
0320                 }
0321 
0322                 return;
0323             }
0324 
0325             // reset
0326             m_degenerated_turn_ptr = NULL;
0327 
0328             // handle possible exit
0329             bool fake_enter_detected = false;
0330             if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
0331             {
0332                 // real exit point - may be multiple
0333                 // we know that we entered and now we exit
0334                 if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(),
0335                                                   *it,
0336                                                   boundary_checker.strategy()) )
0337                 {
0338                     m_exit_watcher.reset_detected_exit();
0339 
0340                     // not the last IP
0341                     update<interior, exterior, '1', transpose_result>(res);
0342                 }
0343                 // fake exit point, reset state
0344                 else if ( op == overlay::operation_intersection )
0345                 {
0346                     m_exit_watcher.reset_detected_exit();
0347                     fake_enter_detected = true;
0348                 }
0349             }
0350             else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
0351             {
0352                 // ignore multiple BLOCKs
0353                 if ( op == overlay::operation_blocked )
0354                     return;
0355 
0356                 if ( op == overlay::operation_intersection
0357                   && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(),
0358                                                 *it,
0359                                                 boundary_checker.strategy()) )
0360                 {
0361                     fake_enter_detected = true;
0362                 }
0363 
0364                 m_exit_watcher.reset_detected_exit();
0365             }
0366 
0367             // i/i, i/x, i/u
0368             if ( op == overlay::operation_intersection )
0369             {
0370                 bool const was_outside = m_exit_watcher.is_outside();
0371                 m_exit_watcher.enter(*it);
0372 
0373                 // interiors overlaps
0374                 update<interior, interior, '1', transpose_result>(res);
0375 
0376                 bool const this_b = it->operations[op_id].position == overlay::position_front // ignore spikes!
0377                                  && is_ip_on_boundary(it->point, it->operations[op_id],
0378                                                       boundary_checker);
0379 
0380                 // going inside on boundary point
0381                 // may be front only
0382                 if ( this_b )
0383                 {
0384                     // may be front and back
0385                     bool const other_b = is_ip_on_boundary(it->point, it->operations[other_op_id],
0386                                                            other_boundary_checker);
0387 
0388                     // it's also the boundary of the other geometry
0389                     if ( other_b )
0390                     {
0391                         update<boundary, boundary, '0', transpose_result>(res);
0392                     }
0393                     else
0394                     {
0395                         update<boundary, interior, '0', transpose_result>(res);
0396                     }
0397                 }
0398                 // going inside on non-boundary point
0399                 else
0400                 {
0401                     // if we didn't enter in the past, we were outside
0402                     if ( was_outside
0403                       && ! fake_enter_detected
0404                       && it->operations[op_id].position != overlay::position_front
0405                       && ! m_collinear_spike_exit )
0406                     {
0407                         update<interior, exterior, '1', transpose_result>(res);
0408 
0409                         // if it's the first IP then the first point is outside
0410                         if ( first_in_range )
0411                         {
0412                             bool const front_b = boundary_checker.is_endpoint_boundary(
0413                                                     range::front(sub_range(geometry, seg_id)));
0414 
0415                             // if there is a boundary on the first point
0416                             if ( front_b )
0417                             {
0418                                 update<boundary, exterior, '0', transpose_result>(res);
0419                             }
0420                         }
0421                     }
0422                 }
0423 
0424                 m_collinear_spike_exit = false;
0425             }
0426             // u/i, u/u, u/x, x/i, x/u, x/x
0427             else if ( op == overlay::operation_union || op == overlay::operation_blocked )
0428             {
0429                 // TODO: is exit watcher still needed?
0430                 // couldn't is_collinear and some going inside counter be used instead?
0431 
0432                 bool const is_collinear = it->operations[op_id].is_collinear;
0433                 bool const was_outside = m_exit_watcher.is_outside()
0434                                       && m_exit_watcher.get_exit_operation() == overlay::operation_none;
0435 // TODO: move the above condition into the exit_watcher?
0436 
0437                 // to exit we must be currently inside and the current segment must be collinear
0438                 if ( !was_outside && is_collinear )
0439                 {
0440                     m_exit_watcher.exit(*it, false);
0441                     // if the position is not set to back it must be a spike
0442                     if ( it->operations[op_id].position != overlay::position_back )
0443                     {
0444                         m_collinear_spike_exit = true;
0445                     }
0446                 }
0447 
0448                 bool const op_blocked = op == overlay::operation_blocked;
0449 
0450                 // we're inside and going out from inside
0451                 // possibly going out right now
0452                 if ( ! was_outside && is_collinear )
0453                 {
0454                     if ( op_blocked
0455                       && it->operations[op_id].position == overlay::position_back ) // ignore spikes!
0456                     {
0457                         // check if this is indeed the boundary point
0458                         // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
0459                         if (boundary_checker.is_endpoint_boundary(it->point))
0460                         {
0461                             // may be front and back
0462                             bool const other_b = is_ip_on_boundary(it->point,
0463                                                                    it->operations[other_op_id],
0464                                                                    other_boundary_checker);
0465                             // it's also the boundary of the other geometry
0466                             if ( other_b )
0467                             {
0468                                 update<boundary, boundary, '0', transpose_result>(res);
0469                             }
0470                             else
0471                             {
0472                                 update<boundary, interior, '0', transpose_result>(res);
0473                             }
0474                         }
0475                     }
0476                 }
0477                 // we're outside or intersects some segment from the outside
0478                 else
0479                 {
0480                     // if we are truly outside
0481                     if ( was_outside
0482                       && it->operations[op_id].position != overlay::position_front
0483                       && ! m_collinear_spike_exit
0484                       /*&& !is_collinear*/ )
0485                     {
0486                         update<interior, exterior, '1', transpose_result>(res);
0487                     }
0488 
0489                     // boundaries don't overlap - just an optimization
0490                     if ( it->method == overlay::method_crosses )
0491                     {
0492                         // the L1 is going from one side of the L2 to the other through the point
0493                         update<interior, interior, '0', transpose_result>(res);
0494 
0495                         // it's the first point in range
0496                         if ( first_in_range )
0497                         {
0498                             bool const front_b = boundary_checker.is_endpoint_boundary(
0499                                                     range::front(sub_range(geometry, seg_id)));
0500 
0501                             // if there is a boundary on the first point
0502                             if ( front_b )
0503                             {
0504                                 update<boundary, exterior, '0', transpose_result>(res);
0505                             }
0506                         }
0507                     }
0508                     // method other than crosses, check more conditions
0509                     else
0510                     {
0511                         bool const this_b = is_ip_on_boundary(it->point,
0512                                                               it->operations[op_id],
0513                                                               boundary_checker);
0514 
0515                         bool const other_b = is_ip_on_boundary(it->point,
0516                                                                it->operations[other_op_id],
0517                                                                other_boundary_checker);
0518 
0519                         // if current IP is on boundary of the geometry
0520                         if ( this_b )
0521                         {
0522                             // it's also the boundary of the other geometry
0523                             if ( other_b )
0524                             {
0525                                 update<boundary, boundary, '0', transpose_result>(res);
0526                             }
0527                             else
0528                             {
0529                                 update<boundary, interior, '0', transpose_result>(res);
0530                             }
0531                         }
0532                         // if current IP is not on boundary of the geometry
0533                         else
0534                         {
0535                             // it's also the boundary of the other geometry
0536                             if ( other_b )
0537                             {
0538                                 update<interior, boundary, '0', transpose_result>(res);
0539                             }
0540                             else
0541                             {
0542                                 update<interior, interior, '0', transpose_result>(res);
0543                             }
0544                         }
0545 
0546                         // first IP on the last segment point - this means that the first point is outside
0547                         if ( first_in_range
0548                           && ( !this_b || op_blocked )
0549                           && was_outside
0550                           && it->operations[op_id].position != overlay::position_front
0551                           && ! m_collinear_spike_exit
0552                           /*&& !is_collinear*/ )
0553                         {
0554                             bool const front_b = boundary_checker.is_endpoint_boundary(
0555                                                     range::front(sub_range(geometry, seg_id)));
0556 
0557                             // if there is a boundary on the first point
0558                             if ( front_b )
0559                             {
0560                                 update<boundary, exterior, '0', transpose_result>(res);
0561                             }
0562                         }
0563 
0564                     }
0565                 }
0566             }
0567 
0568             // store ref to previously analysed (valid) turn
0569             m_previous_turn_ptr = boost::addressof(*it);
0570             // and previously analysed (valid) operation
0571             m_previous_operation = op;
0572         }
0573 
0574         // Called for last
0575         template <typename Result,
0576                   typename TurnIt,
0577                   typename Geometry,
0578                   typename OtherGeometry,
0579                   typename BoundaryChecker,
0580                   typename OtherBoundaryChecker>
0581         void apply(Result & res,
0582                    TurnIt first, TurnIt last,
0583                    Geometry const& geometry,
0584                    OtherGeometry const& /*other_geometry*/,
0585                    BoundaryChecker const& boundary_checker,
0586                    OtherBoundaryChecker const& /*other_boundary_checker*/)
0587         {
0588             boost::ignore_unused(first, last);
0589             //BOOST_GEOMETRY_ASSERT( first != last );
0590 
0591             // here, the possible exit is the real one
0592             // we know that we entered and now we exit
0593             if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
0594                 ||*/ m_previous_operation == overlay::operation_union
0595                 || m_degenerated_turn_ptr )
0596             {
0597                 update<interior, exterior, '1', transpose_result>(res);
0598 
0599                 BOOST_GEOMETRY_ASSERT(first != last);
0600 
0601                 const TurnInfo * turn_ptr = NULL;
0602                 if ( m_degenerated_turn_ptr )
0603                     turn_ptr = m_degenerated_turn_ptr;
0604                 else if ( m_previous_turn_ptr )
0605                     turn_ptr = m_previous_turn_ptr;
0606 
0607                 if ( turn_ptr )
0608                 {
0609                     segment_identifier const& prev_seg_id = turn_ptr->operations[op_id].seg_id;
0610 
0611                     //BOOST_GEOMETRY_ASSERT(!boost::empty(sub_range(geometry, prev_seg_id)));
0612                     bool const prev_back_b = boundary_checker.is_endpoint_boundary(
0613                                                 range::back(sub_range(geometry, prev_seg_id)));
0614 
0615                     // if there is a boundary on the last point
0616                     if ( prev_back_b )
0617                     {
0618                         update<boundary, exterior, '0', transpose_result>(res);
0619                     }
0620                 }
0621             }
0622 
0623             // Just in case,
0624             // reset exit watcher before the analysis of the next Linestring
0625             // note that if there are some enters stored there may be some error above
0626             m_exit_watcher.reset();
0627 
0628             m_previous_turn_ptr = NULL;
0629             m_previous_operation = overlay::operation_none;
0630             m_degenerated_turn_ptr = NULL;
0631 
0632             // actually if this is set to true here there is some error
0633             // in get_turns_ll or relate_ll, an assert could be checked here
0634             m_collinear_spike_exit = false;
0635         }
0636 
0637         template <typename Result,
0638                   typename Turn,
0639                   typename Geometry,
0640                   typename OtherGeometry,
0641                   typename BoundaryChecker,
0642                   typename OtherBoundaryChecker>
0643         void handle_degenerated(Result & res,
0644                                 Turn const& turn,
0645                                 Geometry const& geometry,
0646                                 OtherGeometry const& other_geometry,
0647                                 BoundaryChecker const& boundary_checker,
0648                                 OtherBoundaryChecker const& other_boundary_checker,
0649                                 bool first_in_range)
0650         {
0651             auto const& ls1 = detail::single_geometry(geometry, turn.operations[op_id].seg_id);
0652             auto const& ls2 = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id);
0653 
0654             // only one of those should be true:
0655 
0656             if ( turn.operations[op_id].position == overlay::position_front )
0657             {
0658                 // valid, point-sized
0659                 if ( boost::size(ls2) == 2 )
0660                 {
0661                     bool const front_b = boundary_checker.is_endpoint_boundary(turn.point);
0662 
0663                     if ( front_b )
0664                     {
0665                         update<boundary, interior, '0', transpose_result>(res);
0666                     }
0667                     else
0668                     {
0669                         update<interior, interior, '0', transpose_result>(res);
0670                     }
0671 
0672                     // operation 'c' should be last for the same IP so we know that the next point won't be the same
0673                     update<interior, exterior, '1', transpose_result>(res);
0674 
0675                     m_degenerated_turn_ptr = boost::addressof(turn);
0676                 }
0677             }
0678             else if ( turn.operations[op_id].position == overlay::position_back )
0679             {
0680                 // valid, point-sized
0681                 if ( boost::size(ls2) == 2 )
0682                 {
0683                     update<interior, exterior, '1', transpose_result>(res);
0684 
0685                     bool const back_b = boundary_checker.is_endpoint_boundary(turn.point);
0686 
0687                     if ( back_b )
0688                     {
0689                         update<boundary, interior, '0', transpose_result>(res);
0690                     }
0691                     else
0692                     {
0693                         update<interior, interior, '0', transpose_result>(res);
0694                     }
0695 
0696                     if ( first_in_range )
0697                     {
0698                         //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1));
0699                         bool const front_b = boundary_checker.is_endpoint_boundary(
0700                                                 range::front(ls1));
0701                         if ( front_b )
0702                         {
0703                             update<boundary, exterior, '0', transpose_result>(res);
0704                         }
0705                     }
0706                 }
0707             }
0708             else if ( turn.operations[op_id].position == overlay::position_middle
0709                    && turn.operations[other_op_id].position == overlay::position_middle )
0710             {
0711                 update<interior, interior, '0', transpose_result>(res);
0712 
0713                 // here we don't know which one is degenerated
0714 
0715                 bool const is_point1 = boost::size(ls1) == 2
0716                                     && equals::equals_point_point(range::front(ls1),
0717                                                                   range::back(ls1),
0718                                                                   boundary_checker.strategy());
0719                 bool const is_point2 = boost::size(ls2) == 2
0720                                     && equals::equals_point_point(range::front(ls2),
0721                                                                   range::back(ls2),
0722                                                                   other_boundary_checker.strategy());
0723 
0724                 // if the second one is degenerated
0725                 if ( !is_point1 && is_point2 )
0726                 {
0727                     update<interior, exterior, '1', transpose_result>(res);
0728 
0729                     if ( first_in_range )
0730                     {
0731                         //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1));
0732                         bool const front_b = boundary_checker.is_endpoint_boundary(
0733                                                 range::front(ls1));
0734                         if ( front_b )
0735                         {
0736                             update<boundary, exterior, '0', transpose_result>(res);
0737                         }
0738                     }
0739 
0740                     m_degenerated_turn_ptr = boost::addressof(turn);
0741                 }
0742             }
0743 
0744             // NOTE: other.position == front and other.position == back
0745             //       will be handled later, for the other geometry
0746         }
0747 
0748     private:
0749         exit_watcher<TurnInfo, OpId> m_exit_watcher;
0750         segment_watcher<same_single> m_seg_watcher;
0751         const TurnInfo * m_previous_turn_ptr;
0752         overlay::operation_type m_previous_operation;
0753         const TurnInfo * m_degenerated_turn_ptr;
0754         bool m_collinear_spike_exit;
0755     };
0756 
0757     template <typename Result,
0758               typename TurnIt,
0759               typename Analyser,
0760               typename Geometry,
0761               typename OtherGeometry,
0762               typename BoundaryChecker,
0763               typename OtherBoundaryChecker>
0764     static inline void analyse_each_turn(Result & res,
0765                                          Analyser & analyser,
0766                                          TurnIt first, TurnIt last,
0767                                          Geometry const& geometry,
0768                                          OtherGeometry const& other_geometry,
0769                                          BoundaryChecker const& boundary_checker,
0770                                          OtherBoundaryChecker const& other_boundary_checker)
0771     {
0772         if ( first == last )
0773             return;
0774 
0775         for ( TurnIt it = first ; it != last ; ++it )
0776         {
0777             analyser.apply(res, it,
0778                            geometry, other_geometry,
0779                            boundary_checker, other_boundary_checker);
0780 
0781             if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
0782                 return;
0783         }
0784 
0785         analyser.apply(res, first, last,
0786                        geometry, other_geometry,
0787                        boundary_checker, other_boundary_checker);
0788     }
0789 };
0790 
0791 }} // namespace detail::relate
0792 #endif // DOXYGEN_NO_DETAIL
0793 
0794 }} // namespace boost::geometry
0795 
0796 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP