]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / geometry / algorithms / detail / overlay / get_turn_info.hpp
index 12e2b0d351d535c3d170112ace053f8760397cd7..e90910c9bca44ae35aa0a8c02147e5c471ad14d3 100644 (file)
@@ -111,6 +111,60 @@ struct base_turn_handler
         both(ti, condition ? operation_union : operation_intersection);
     }
 
+
+#if ! defined(BOOST_GEOMETRY_USE_RESCALING)
+    template
+    <
+        typename UniqueSubRange1,
+        typename UniqueSubRange2
+    >
+    static inline int side_with_distance_measure(UniqueSubRange1 const& range_p,
+            UniqueSubRange2 const& range_q,
+            int range_index, int point_index)
+    {
+        if (range_index >= 1 && range_p.is_last_segment())
+        {
+            return 0;
+        }
+        if (point_index >= 2 && range_q.is_last_segment())
+        {
+            return 0;
+        }
+
+        typedef typename select_coordinate_type
+            <
+                typename UniqueSubRange1::point_type,
+                typename UniqueSubRange2::point_type
+            >::type coordinate_type;
+
+        typedef detail::distance_measure<coordinate_type> dm_type;
+
+        dm_type const dm = get_distance_measure(range_p.at(range_index), range_p.at(range_index + 1), range_q.at(point_index));
+        return dm.measure == 0 ? 0 : dm.measure > 0 ? 1 : -1;
+    }
+
+    template
+    <
+        typename UniqueSubRange1,
+        typename UniqueSubRange2
+    >
+    static inline int verified_side(int side,
+                                    UniqueSubRange1 const& range_p,
+                                    UniqueSubRange2 const& range_q,
+                                    int range_index,
+                                    int point_index)
+    {
+        return side == 0 ? side_with_distance_measure(range_p, range_q, range_index, point_index) : side;
+    }
+#else
+    template <typename T1, typename T2>
+    static inline int verified_side(int side, T1 const& , T2 const& , int , int)
+    {
+        return side;
+    }
+#endif
+
+
     template <typename TurnInfo, typename IntersectionInfo>
     static inline void assign_point(TurnInfo& ti,
                 method_type method,
@@ -125,6 +179,39 @@ struct base_turn_handler
         ti.operations[1].fraction = info.fractions[index].robust_rb;
     }
 
+    template <typename TurnInfo, typename IntersectionInfo, typename DirInfo>
+    static inline void assign_point_and_correct(TurnInfo& ti,
+                method_type method,
+                IntersectionInfo const& info, DirInfo const& dir_info)
+    {
+        ti.method = method;
+
+        // For touch/touch interior always take the intersection point 0 (there is only one).
+        static int const index = 0;
+
+        geometry::convert(info.intersections[index], ti.point);
+
+        for (int i = 0; i < 2; i++)
+        {
+            if (dir_info.arrival[i] == 1)
+            {
+                // The segment arrives at the intersection point, its fraction should be 1
+                // (due to precision it might be nearly so, but not completely, in rare cases)
+                ti.operations[i].fraction = {1, 1};
+            }
+            else if (dir_info.arrival[i] == -1)
+            {
+                // The segment leaves from the intersection point, likewise its fraction should be 0
+                ti.operations[i].fraction = {0, 1};
+            }
+            else
+            {
+                ti.operations[i].fraction = i == 0 ? info.fractions[index].robust_ra
+                                                   : info.fractions[index].robust_rb;
+            }
+        }
+    }
+
     template <typename IntersectionInfo>
     static inline unsigned int non_opposite_to_index(IntersectionInfo const& info)
     {
@@ -178,7 +265,7 @@ struct base_turn_handler
                 <
                     typename UniqueSubRange1::point_type,
                     typename UniqueSubRange2::point_type
-                    >::type
+                >::type
             > dm_type;
 
         const bool p_closer =
@@ -186,9 +273,9 @@ struct base_turn_handler
                 <  ti.operations[IndexQ].remaining_distance;
         dm_type const dm
                 = p_closer
-                ? get_distance_measure<typename UmbrellaStrategy::cs_tag>(range_q.at(index_q - 1),
+                ? get_distance_measure(range_q.at(index_q - 1),
                     range_q.at(index_q), range_p.at(index_p))
-                : get_distance_measure<typename UmbrellaStrategy::cs_tag>(range_p.at(index_p - 1),
+                : get_distance_measure(range_p.at(index_p - 1),
                     range_p.at(index_p), range_q.at(index_q));
 
         if (! dm.is_zero())
@@ -218,6 +305,59 @@ template
 >
 struct touch_interior : public base_turn_handler
 {
+
+    template
+    <
+        typename IntersectionInfo,
+        typename UniqueSubRange
+    >
+    static bool handle_as_touch(IntersectionInfo const& info,
+                                UniqueSubRange const& non_touching_range)
+    {
+#if defined(BOOST_GEOMETRY_USE_RESCALING)
+        return false;
+#endif
+        //
+        //
+        //                         ^  Q(i)                ^ P(i)
+        //                          \                    /
+        //                           \                  /
+        //                            \                /
+        //                             \              /
+        //                              \            /
+        //                               \          /
+        //                                \        /
+        //                                 \      /
+        //                                  \    /
+        //                                   \  / it is about buffer_rt_r
+        //                  P(k)              v/  they touch here "in the middle", but at the intersection...
+        //                  <---------------->v   there is no follow up IP
+        //                                   /
+        //                                  /
+        //                                 /
+        //                                /
+        //                               /
+        //                              /
+        //                             v Q(k)
+        //
+
+        // Measure where the IP is located. If it is really close to the end,
+        // then there is no space for the next IP (on P(1)/Q(2). A "from"
+        // intersection will be generated, but those are never handled.
+        // Therefore handle it as a normal touch (two segments arrive at the
+        // intersection point). It currently checks for zero, but even a
+        // distance a little bit larger would do.
+        typedef typename geometry::coordinate_type
+            <
+                typename UniqueSubRange::point_type
+            >::type coor_t;
+
+        coor_t const location = distance_measure(info.intersections[0], non_touching_range.at(1));
+        coor_t const zero = 0;
+        bool const result = math::equals(location, zero);
+        return result;
+    }
+
     // Index: 0, P is the interior, Q is touching and vice versa
     template
     <
@@ -237,7 +377,7 @@ struct touch_interior : public base_turn_handler
                 SidePolicy const& side,
                 UmbrellaStrategy const& umbrella_strategy)
     {
-        assign_point(ti, method_touch_interior, intersection_info, 0);
+        assign_point_and_correct(ti, method_touch_interior, intersection_info, dir_info);
 
         // Both segments of q touch segment p somewhere in its interior
         // 1) We know: if q comes from LEFT or RIGHT
@@ -336,7 +476,6 @@ struct touch_interior : public base_turn_handler
             if (side_qk_q == side_qi_p)
             {
                 both_collinear<index_p, index_q>(range_p, range_q, umbrella_strategy, 1, 2, ti);
-                return;
             }
             else
             {
@@ -369,14 +508,80 @@ struct touch : public base_turn_handler
         return side1 == side2 && ! opposite(side1, turn);
     }
 
-    /*static inline void block_second(bool block, TurnInfo& ti)
+#if ! defined(BOOST_GEOMETRY_USE_RESCALING)
+    template
+    <
+        typename UniqueSubRange1,
+        typename UniqueSubRange2
+    >
+    static inline bool handle_imperfect_touch(UniqueSubRange1 const& range_p,
+            UniqueSubRange2 const& range_q, TurnInfo& ti)
     {
-        if (block)
+        //  Q
+        //  ^
+        // ||
+        // ||
+        // |^----
+        // >----->P
+        // *            * they touch here (P/Q are (nearly) on top)
+        //
+        // Q continues from where P comes.
+        // P continues from where Q comes
+        // This is often a blocking situation,
+        // unless there are FP issues: there might be a distance
+        // between Pj and Qj, in that case handle it as a union.
+        //
+        // Exaggerated:
+        //  Q
+        //  ^           Q is nearly vertical
+        //   \          but not completely - and still ends above P
+        // |  \qj       In this case it should block P and
+        // |  ^------   set Q to Union
+        // >----->P     qj is LEFT of P1 and pi is LEFT of Q2
+        //              (the other way round is also possible)
+
+        typedef typename select_coordinate_type
+            <
+                typename UniqueSubRange1::point_type,
+                typename UniqueSubRange2::point_type
+            >::type coordinate_type;
+
+        typedef detail::distance_measure<coordinate_type> dm_type;
+
+        dm_type const dm_qj_p1 = get_distance_measure(range_p.at(0), range_p.at(1), range_q.at(1));
+        dm_type const dm_pi_q2 = get_distance_measure(range_q.at(1), range_q.at(2), range_p.at(0));
+
+        if (dm_qj_p1.measure > 0 && dm_pi_q2.measure > 0)
         {
-            ti.operations[1].operation = operation_blocked;
+            // Even though there is a touch, Q(j) is left of P1
+            // and P(i) is still left from Q2.
+            // It can continue.
+            ti.operations[0].operation = operation_blocked;
+            // Q turns right -> union (both independent),
+            // Q turns left -> intersection
+            ti.operations[1].operation = operation_union;
+            ti.touch_only = true;
+            return true;
         }
-    }*/
 
+        dm_type const dm_pj_q1 = get_distance_measure(range_q.at(0), range_q.at(1), range_p.at(1));
+        dm_type const dm_qi_p2 = get_distance_measure(range_p.at(1), range_p.at(2), range_q.at(0));
+
+        if (dm_pj_q1.measure > 0 && dm_qi_p2.measure > 0)
+        {
+            // Even though there is a touch, Q(j) is left of P1
+            // and P(i) is still left from Q2.
+            // It can continue.
+            ti.operations[0].operation = operation_union;
+            // Q turns right -> union (both independent),
+            // Q turns left -> intersection
+            ti.operations[1].operation = operation_blocked;
+            ti.touch_only = true;
+            return true;
+        }
+        return false;
+    }
+#endif
 
     template
     <
@@ -395,14 +600,15 @@ struct touch : public base_turn_handler
                 SideCalculator const& side,
                 UmbrellaStrategy const& umbrella_strategy)
     {
-        assign_point(ti, method_touch, intersection_info, 0);
+        assign_point_and_correct(ti, method_touch, intersection_info, dir_info);
 
         bool const has_pk = ! range_p.is_last_segment();
         bool const has_qk = ! range_q.is_last_segment();
 
-        int const side_qi_p1 = dir_info.sides.template get<1, 0>();
-        int const side_qk_p1 = has_qk ? side.qk_wrt_p1() : 0;
+        int const side_pk_q1 = has_pk ? side.pk_wrt_q1() : 0;
 
+        int const side_qi_p1 = verified_side(dir_info.sides.template get<1, 0>(), range_p, range_q, 0, 0);
+        int const side_qk_p1 = has_qk ? verified_side(side.qk_wrt_p1(), range_p, range_q, 0, 2) : 0;
 
         // If Qi and Qk are both at same side of Pi-Pj,
         // or collinear (so: not opposite sides)
@@ -413,6 +619,7 @@ struct touch : public base_turn_handler
             int const side_qk_q  = has_qk ? side.qk_wrt_q1() : 0;
 
             bool const q_turns_left = side_qk_q == 1;
+
             bool const block_q = side_qk_p1 == 0
                         && ! same(side_qi_p1, side_qk_q)
                         ;
@@ -422,9 +629,18 @@ struct touch : public base_turn_handler
             // or Q is fully collinear && P turns not to left
             if (side_pk_p == side_qi_p1
                 || side_pk_p == side_qk_p1
-                || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1)
-                )
+                || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1))
             {
+#if ! defined(BOOST_GEOMETRY_USE_RESCALING)
+                if (side_qk_p1 == 0 && side_pk_q1 == 0
+                    && has_qk && has_qk
+                    && handle_imperfect_touch(range_p, range_q, ti))
+                {
+                    // If q continues collinearly (opposite) with p, it should be blocked
+                    // but (FP) not if there is just a tiny space in between
+                    return;
+                }
+#endif
                 // Collinear -> lines join, continue
                 // (#BRL2)
                 if (side_pk_q2 == 0 && ! block_q)
@@ -433,8 +649,6 @@ struct touch : public base_turn_handler
                     return;
                 }
 
-                int const side_pk_q1 = has_pk && has_qk ? side.pk_wrt_q1() : 0;
-
                 // Collinear opposite case -> block P
                 // (#BRL4, #BLR8)
                 if (side_pk_q1 == 0)
@@ -457,7 +671,6 @@ struct touch : public base_turn_handler
                     {
                         ti.operations[1].operation = operation_blocked;
                     }
-                    //block_second(block_q, ti);
                     return;
                 }
 
@@ -483,7 +696,6 @@ struct touch : public base_turn_handler
                     {
                         ti.touch_only = true;
                     }
-                    //block_second(block_q, ti);
                     return;
                 }
             }
@@ -508,8 +720,9 @@ struct touch : public base_turn_handler
         }
         else
         {
+            // The qi/qk are opposite to each other, w.r.t. p1
             // From left to right or from right to left
-            int const side_pk_p = has_pk ? side.pk_wrt_p1() : 0;
+            int const side_pk_p = has_pk ? verified_side(side.pk_wrt_p1(), range_p, range_p, 0, 2) : 0;
             bool const right_to_left = side_qk_p1 == 1;
 
             // If p turns into direction of qi (1,2)
@@ -579,7 +792,7 @@ struct equal : public base_turn_handler
                 UniqueSubRange2 const& range_q,
                 TurnInfo& ti,
                 IntersectionInfo const& info,
-                DirInfo const&  ,
+                DirInfo const& ,
                 SideCalculator const& side,
                 UmbrellaStrategy const& umbrella_strategy)
     {
@@ -601,23 +814,23 @@ struct equal : public base_turn_handler
             // Without rescaling, to check for union/intersection,
             // try to check side values (without any thresholds)
             typedef typename select_coordinate_type
-                    <
-                        typename UniqueSubRange1::point_type,
-                        typename UniqueSubRange2::point_type
-                        >::type coordinate_type;
+                <
+                    typename UniqueSubRange1::point_type,
+                    typename UniqueSubRange2::point_type
+                >::type coordinate_type;
 
             typedef detail::distance_measure<coordinate_type> dm_type;
 
-            dm_type const dm_qk_p
-               = get_distance_measure<typename UmbrellaStrategy::cs_tag>(range_q.at(1), range_q.at(2), range_p.at(2));
-            dm_type const dm_pk_q
-               = get_distance_measure<typename UmbrellaStrategy::cs_tag>(range_p.at(1), range_p.at(2), range_q.at(2));
+            dm_type const dm_pk_q2
+               = get_distance_measure(range_q.at(1), range_q.at(2), range_p.at(2));
+            dm_type const dm_qk_p2
+               = get_distance_measure(range_p.at(1), range_p.at(2), range_q.at(2));
 
-            if (dm_pk_q.measure != dm_qk_p.measure)
+            if (dm_qk_p2.measure != dm_pk_q2.measure)
             {
                 // A (possibly very small) difference is detected, which
                 // can be used to distinguish between union/intersection
-                ui_else_iu(dm_pk_q.measure < dm_qk_p.measure, ti);
+                ui_else_iu(dm_qk_p2.measure < dm_pk_q2.measure, ti);
                 return;
             }
         }
@@ -649,6 +862,65 @@ struct equal : public base_turn_handler
     }
 };
 
+template
+<
+    typename TurnInfo
+>
+struct start : public base_turn_handler
+{
+    template
+    <
+        typename UniqueSubRange1,
+        typename UniqueSubRange2,
+        typename IntersectionInfo,
+        typename DirInfo,
+        typename SideCalculator,
+        typename UmbrellaStrategy
+    >
+    static inline bool apply(UniqueSubRange1 const& /*range_p*/,
+                UniqueSubRange2 const& /*range_q*/,
+                TurnInfo& ti,
+                IntersectionInfo const& info,
+                DirInfo const& dir_info,
+                SideCalculator const& side,
+                UmbrellaStrategy const& )
+    {
+#if defined(BOOST_GEOMETRY_USE_RESCALING)
+        // With rescaling, start turns are not needed.
+        return false;
+#endif
+
+        // Start turns have either how_a = -1, or how_b = -1 (either p leaves or q leaves)
+        BOOST_GEOMETRY_ASSERT(dir_info.how_a != dir_info.how_b);
+        BOOST_GEOMETRY_ASSERT(dir_info.how_a == -1 || dir_info.how_b == -1);
+        BOOST_GEOMETRY_ASSERT(dir_info.how_a == 0 || dir_info.how_b == 0);
+
+        if (dir_info.how_b == -1)
+        {
+            // p --------------->
+            //             |
+            //             | q         q leaves
+            //             v
+            //
+
+            int const side_qj_p1 = side.qj_wrt_p1();
+            ui_else_iu(side_qj_p1 == -1, ti);
+        }
+        else if (dir_info.how_a == -1)
+        {
+            // p leaves
+            int const side_pj_q1 = side.pj_wrt_q1();
+            ui_else_iu(side_pj_q1 == 1, ti);
+        }
+
+        // Copy intersection point
+        assign_point_and_correct(ti, method_start, info, dir_info);
+        return true;
+    }
+
+};
+
+
 template
 <
     typename TurnInfo,
@@ -820,23 +1092,15 @@ private :
          1         -1         -1               CXO3    ux
     */
 
-    template
-    <
-        unsigned int Index,
-        typename IntersectionInfo
-    >
-    static inline bool set_tp(int side_rk_r, bool handle_robustness,
-                int side_rk_s,
-                TurnInfo& tp, IntersectionInfo const& intersection_info)
+    template <unsigned int Index, typename IntersectionInfo>
+    static inline bool set_tp(int side_rk_r, TurnInfo& tp,
+                              IntersectionInfo const& intersection_info)
     {
         BOOST_STATIC_ASSERT(Index <= 1);
 
-        boost::ignore_unused(handle_robustness, side_rk_s);
-
         operation_type blocked = operation_blocked;
         switch(side_rk_r)
         {
-
             case 1 :
                 // Turning left on opposite collinear: intersection
                 tp.operations[Index].operation = operation_intersection;
@@ -899,8 +1163,6 @@ public:
               tp_model, out, intersection_info, side, empty_transformer);
     }
 
-public:
-
     template
     <
         typename UniqueSubRange1,
@@ -930,7 +1192,7 @@ public:
         // If P arrives within Q, there is a turn dependent on P
         if ( p_arrival == 1
           && ! range_p.is_last_segment()
-          && set_tp<0>(side.pk_wrt_p1(), true, side.pk_wrt_q1(), tp, info.i_info()) )
+          && set_tp<0>(side.pk_wrt_p1(), tp, info.i_info()) )
         {
             turn_transformer(tp);
 
@@ -940,7 +1202,7 @@ public:
         // If Q arrives within P, there is a turn dependent on Q
         if ( q_arrival == 1
           && ! range_q.is_last_segment()
-          && set_tp<1>(side.qk_wrt_q1(), false, side.qk_wrt_p1(), tp, info.i_info()) )
+          && set_tp<1>(side.qk_wrt_q1(), tp, info.i_info()) )
         {
             turn_transformer(tp);
 
@@ -999,7 +1261,7 @@ struct only_convert : public base_turn_handler
     template<typename TurnInfo, typename IntersectionInfo>
     static inline void apply(TurnInfo& ti, IntersectionInfo const& intersection_info)
     {
-        assign_point(ti, method_none, intersection_info, 0); // was collinear
+        assign_point(ti, method_none, intersection_info, 0);
         ti.operations[0].operation = operation_continue;
         ti.operations[1].operation = operation_continue;
     }
@@ -1015,6 +1277,15 @@ struct assign_null_policy
     static bool const include_no_turn = false;
     static bool const include_degenerate = false;
     static bool const include_opposite = false;
+    static bool const include_start_turn = false;
+};
+
+struct assign_policy_only_start_turns
+{
+    static bool const include_no_turn = false;
+    static bool const include_degenerate = false;
+    static bool const include_opposite = false;
+    static bool const include_start_turn = true;
 };
 
 /*!
@@ -1063,139 +1334,151 @@ struct get_turn_info
 
         char const method = inters.d_info().how;
 
+        if (method == 'd')
+        {
+            // Disjoint
+            return out;
+        }
+
         // Copy, to copy possibly extended fields
         TurnInfo tp = tp_model;
 
-        bool do_only_convert = false;
+        bool const handle_as_touch_interior = method == 'm';
+        bool const handle_as_cross = method == 'i';
+        bool handle_as_touch = method == 't';
+        bool handle_as_equal = method == 'e';
+        bool const handle_as_collinear = method == 'c';
+        bool const handle_as_degenerate = method == '0';
+        bool const handle_as_start = method == 's';
+
+        // (angle, from)
+        bool do_only_convert = method == 'a' || method == 'f';
 
-        // Select method and apply
-        switch(method)
+        if (handle_as_start)
         {
-            case 'a' : // "angle"
-            case 'f' : // "from"
-            case 's' : // "start"
-                do_only_convert = true;
-                break;
+            // It is in some cases necessary to handle a start turn
+            if (AssignPolicy::include_start_turn
+                && start<TurnInfo>::apply(range_p, range_q, tp, inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy))
+            {
+                *out++ = tp;
+            }
+            else
+            {
+              do_only_convert = true;
+            }
+        }
 
-            case 'd' : // disjoint: never do anything
-                break;
+        if (handle_as_touch_interior)
+        {
+            typedef touch_interior<TurnInfo> handler;
 
-            case 'm' :
+            if ( inters.d_info().arrival[1] == 1 )
             {
-                typedef touch_interior
-                    <
-                        TurnInfo
-                    > handler;
-
-                // If Q (1) arrives (1)
-                if ( inters.d_info().arrival[1] == 1 )
+                // Q arrives
+                if (handler::handle_as_touch(inters.i_info(), range_p))
                 {
-                    handler::template apply<0>(range_p, range_q, tp, inters.i_info(), inters.d_info(),
-                                inters.sides(), umbrella_strategy);
+                    handle_as_touch = true;
                 }
                 else
                 {
-                    // Swap p/q
-                    handler::template apply<1>(range_q, range_p, tp, inters.i_info(), inters.d_info(),
-                                inters.get_swapped_sides(), umbrella_strategy);
+                    handler::template apply<0>(range_p, range_q, tp, inters.i_info(), inters.d_info(),
+                                inters.sides(), umbrella_strategy);
+                    *out++ = tp;
                 }
-                *out++ = tp;
             }
-            break;
-            case 'i' :
-            {
-                crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
-                *out++ = tp;
-            }
-            break;
-            case 't' :
-            {
-                // Both touch (both arrive there)
-                touch<TurnInfo>::apply(range_p, range_q, tp, inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy);
-                *out++ = tp;
-            }
-            break;
-            case 'e':
+            else
             {
-                if ( ! inters.d_info().opposite )
+                // P arrives, swap p/q
+                if (handler::handle_as_touch(inters.i_info(), range_q))
                 {
-                    // Both equal
-                    // or collinear-and-ending at intersection point
-                    equal<TurnInfo>::apply(range_p, range_q, tp, inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy);
-                    *out++ = tp;
+                    handle_as_touch = true;
                 }
                 else
                 {
-                    equal_opposite
-                        <
-                            TurnInfo,
-                            AssignPolicy
-                        >::apply(range_p, range_q, tp, out, inters);
+                    handler::template apply<1>(range_q, range_p, tp, inters.i_info(), inters.d_info(),
+                                inters.get_swapped_sides(), umbrella_strategy);
+                    *out++ = tp;
                 }
             }
-            break;
-            case 'c' :
-            {
-                // Collinear
-                if ( ! inters.d_info().opposite )
-                {
+        }
 
-                    if ( inters.d_info().arrival[0] == 0 )
-                    {
-                        // Collinear, but similar thus handled as equal
-                        equal<TurnInfo>::apply(range_p, range_q, tp,
-                                inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy);
+        if (handle_as_cross)
+        {
+            crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
+            *out++ = tp;
+        }
 
-                        // override assigned method
-                        tp.method = method_collinear;
-                    }
-                    else
-                    {
-                        collinear<TurnInfo>::apply(range_p, range_q, tp,
-                                inters.i_info(), inters.d_info(), inters.sides());
-                    }
+        if (handle_as_touch)
+        {
+            // Touch: both segments arrive at the intersection point
+            touch<TurnInfo>::apply(range_p, range_q, tp, inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy);
+            *out++ = tp;
+        }
 
-                    *out++ = tp;
+        if (handle_as_collinear)
+        {
+            // Collinear
+            if ( ! inters.d_info().opposite )
+            {
+
+                if ( inters.d_info().arrival[0] == 0 )
+                {
+                    handle_as_equal = true;
                 }
                 else
                 {
-                    collinear_opposite
-                        <
-                            TurnInfo,
-                            AssignPolicy
-                        >::apply(range_p, range_q,
-                            tp, out, inters, inters.sides());
+                    collinear<TurnInfo>::apply(range_p, range_q, tp,
+                            inters.i_info(), inters.d_info(), inters.sides());
+                    *out++ = tp;
                 }
             }
-            break;
-            case '0' :
+            else
+            {
+                collinear_opposite
+                    <
+                        TurnInfo,
+                        AssignPolicy
+                    >::apply(range_p, range_q, tp, out, inters, inters.sides());
+                // Zero, or two, turn points are assigned to *out++
+            }
+        }
+
+        if (handle_as_equal)
+        {
+            if ( ! inters.d_info().opposite )
             {
-                // degenerate points
-                if (AssignPolicy::include_degenerate)
+                // Both equal
+                // or collinear-and-ending at intersection point
+                equal<TurnInfo>::apply(range_p, range_q, tp,
+                        inters.i_info(), inters.d_info(), inters.sides(),
+                        umbrella_strategy);
+                if (handle_as_collinear)
                 {
-                    only_convert::apply(tp, inters.i_info());
-                    *out++ = tp;
+                    // Keep info as collinear,
+                    // so override already assigned method
+                    tp.method = method_collinear;
                 }
+                *out++ = tp;
             }
-            break;
-            default :
+            else
             {
-#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
-                std::cout << "TURN: Unknown method: " << method << std::endl;
-#endif
-#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
-                BOOST_THROW_EXCEPTION(turn_info_exception(method));
-#endif
+                equal_opposite
+                    <
+                        TurnInfo,
+                        AssignPolicy
+                    >::apply(range_p, range_q, tp, out, inters);
+                // Zero, or two, turn points are assigned to *out++
             }
-            break;
         }
 
-        if (do_only_convert
-            && AssignPolicy::include_no_turn
-            && inters.i_info().count > 0)
+        if ((handle_as_degenerate && AssignPolicy::include_degenerate)
+            || (do_only_convert && AssignPolicy::include_no_turn))
         {
-            only_convert::apply(tp, inters.i_info());
-            *out++ = tp;
+            if (inters.i_info().count > 0)
+            {
+                only_convert::apply(tp, inters.i_info());
+                *out++ = tp;
+            }
         }
 
         return out;