linear_linear.hpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2013-2022.
  4. // Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP
  10. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP
  11. #include <algorithm>
  12. #include <boost/core/ignore_unused.hpp>
  13. #include <boost/range/size.hpp>
  14. #include <boost/geometry/core/assert.hpp>
  15. #include <boost/geometry/util/condition.hpp>
  16. #include <boost/geometry/util/range.hpp>
  17. #include <boost/geometry/algorithms/detail/sub_range.hpp>
  18. #include <boost/geometry/algorithms/detail/single_geometry.hpp>
  19. #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
  20. #include <boost/geometry/algorithms/detail/relate/result.hpp>
  21. #include <boost/geometry/algorithms/detail/relate/turns.hpp>
  22. #include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
  23. #include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
  24. #include <boost/geometry/geometries/helper_geometry.hpp>
  25. namespace boost { namespace geometry
  26. {
  27. #ifndef DOXYGEN_NO_DETAIL
  28. namespace detail { namespace relate {
  29. template <typename Result, typename BoundaryChecker, bool TransposeResult>
  30. class disjoint_linestring_pred
  31. {
  32. public:
  33. disjoint_linestring_pred(Result & res,
  34. BoundaryChecker const& boundary_checker)
  35. : m_result(res)
  36. , m_boundary_checker(boundary_checker)
  37. , m_flags(0)
  38. {
  39. if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
  40. {
  41. m_flags |= 1;
  42. }
  43. if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
  44. {
  45. m_flags |= 2;
  46. }
  47. }
  48. template <typename Linestring>
  49. bool operator()(Linestring const& linestring)
  50. {
  51. if ( m_flags == 3 )
  52. {
  53. return false;
  54. }
  55. std::size_t const count = boost::size(linestring);
  56. // invalid input
  57. if ( count < 2 )
  58. {
  59. // ignore
  60. // TODO: throw an exception?
  61. return true;
  62. }
  63. // point-like linestring
  64. if ( count == 2
  65. && equals::equals_point_point(range::front(linestring),
  66. range::back(linestring),
  67. m_boundary_checker.strategy()) )
  68. {
  69. update<interior, exterior, '0', TransposeResult>(m_result);
  70. }
  71. else
  72. {
  73. update<interior, exterior, '1', TransposeResult>(m_result);
  74. m_flags |= 1;
  75. // check if there is a boundary
  76. if (m_flags < 2
  77. && (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
  78. || m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
  79. {
  80. update<boundary, exterior, '0', TransposeResult>(m_result);
  81. m_flags |= 2;
  82. }
  83. }
  84. return m_flags != 3
  85. && ! m_result.interrupt;
  86. }
  87. private:
  88. Result & m_result;
  89. BoundaryChecker const& m_boundary_checker;
  90. unsigned m_flags;
  91. };
  92. template <typename Geometry1, typename Geometry2>
  93. struct linear_linear
  94. {
  95. static const bool interruption_enabled = true;
  96. template <typename Result, typename Strategy>
  97. static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
  98. Result & result,
  99. Strategy const& strategy)
  100. {
  101. boundary_checker<Geometry1, Strategy> boundary_checker1(geometry1, strategy);
  102. boundary_checker<Geometry2, Strategy> boundary_checker2(geometry2, strategy);
  103. apply(geometry1, geometry2, boundary_checker1, boundary_checker2, result, strategy);
  104. }
  105. template <typename BoundaryChecker1, typename BoundaryChecker2, typename Result, typename Strategy>
  106. static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
  107. BoundaryChecker1 const& boundary_checker1,
  108. BoundaryChecker2 const& boundary_checker2,
  109. Result & result,
  110. Strategy const& strategy)
  111. {
  112. // The result should be FFFFFFFFF
  113. update<exterior, exterior, result_dimension<Geometry1>::value>(result);// FFFFFFFFd, d in [1,9] or T
  114. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  115. return;
  116. // get and analyse turns
  117. using turn_type = typename turns::get_turns
  118. <
  119. Geometry1, Geometry2
  120. >::template turn_info_type<Strategy>::type;
  121. std::vector<turn_type> turns;
  122. interrupt_policy_linear_linear<Result> interrupt_policy(result);
  123. turns::get_turns
  124. <
  125. Geometry1,
  126. Geometry2,
  127. detail::get_turns::get_turn_info_type<Geometry1, Geometry2, turns::assign_policy<true> >
  128. >::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
  129. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  130. return;
  131. disjoint_linestring_pred<Result, BoundaryChecker1, false> pred1(result, boundary_checker1);
  132. for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
  133. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  134. return;
  135. disjoint_linestring_pred<Result, BoundaryChecker2, true> pred2(result, boundary_checker2);
  136. for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
  137. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  138. return;
  139. if ( turns.empty() )
  140. return;
  141. // TODO: turns must be sorted and followed only if it's possible to go out and in on the same point
  142. // for linear geometries union operation must be detected which I guess would be quite often
  143. if ( may_update<interior, interior, '1'>(result)
  144. || may_update<interior, boundary, '0'>(result)
  145. || may_update<interior, exterior, '1'>(result)
  146. || may_update<boundary, interior, '0'>(result)
  147. || may_update<boundary, boundary, '0'>(result)
  148. || may_update<boundary, exterior, '0'>(result) )
  149. {
  150. using less_t = turns::less<0, turns::less_op_linear_linear<0>, Strategy>;
  151. std::sort(turns.begin(), turns.end(), less_t());
  152. turns_analyser<turn_type, 0> analyser;
  153. analyse_each_turn(result, analyser,
  154. turns.begin(), turns.end(),
  155. geometry1, geometry2,
  156. boundary_checker1, boundary_checker2);
  157. }
  158. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  159. return;
  160. if ( may_update<interior, interior, '1', true>(result)
  161. || may_update<interior, boundary, '0', true>(result)
  162. || may_update<interior, exterior, '1', true>(result)
  163. || may_update<boundary, interior, '0', true>(result)
  164. || may_update<boundary, boundary, '0', true>(result)
  165. || may_update<boundary, exterior, '0', true>(result) )
  166. {
  167. using less_t = turns::less<1, turns::less_op_linear_linear<1>, Strategy>;
  168. std::sort(turns.begin(), turns.end(), less_t());
  169. turns_analyser<turn_type, 1> analyser;
  170. analyse_each_turn(result, analyser,
  171. turns.begin(), turns.end(),
  172. geometry2, geometry1,
  173. boundary_checker2, boundary_checker1);
  174. }
  175. }
  176. template <typename Result>
  177. class interrupt_policy_linear_linear
  178. {
  179. public:
  180. static bool const enabled = true;
  181. explicit interrupt_policy_linear_linear(Result & result)
  182. : m_result(result)
  183. {}
  184. // TODO: since we update result for some operations here, we may not do it in the analyser!
  185. template <typename Range>
  186. inline bool apply(Range const& turns)
  187. {
  188. for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
  189. {
  190. if ( it->operations[0].operation == overlay::operation_intersection
  191. || it->operations[1].operation == overlay::operation_intersection )
  192. {
  193. update<interior, interior, '1'>(m_result);
  194. }
  195. else if ( ( it->operations[0].operation == overlay::operation_union
  196. || it->operations[0].operation == overlay::operation_blocked
  197. || it->operations[1].operation == overlay::operation_union
  198. || it->operations[1].operation == overlay::operation_blocked )
  199. && it->operations[0].position == overlay::position_middle
  200. && it->operations[1].position == overlay::position_middle )
  201. {
  202. // TODO: here we could also check the boundaries and set IB,BI,BB at this point
  203. update<interior, interior, '0'>(m_result);
  204. }
  205. }
  206. return m_result.interrupt;
  207. }
  208. private:
  209. Result & m_result;
  210. };
  211. // This analyser should be used like Input or SinglePass Iterator
  212. template <typename TurnInfo, std::size_t OpId>
  213. class turns_analyser
  214. {
  215. typedef typename TurnInfo::point_type turn_point_type;
  216. static const std::size_t op_id = OpId;
  217. static const std::size_t other_op_id = (OpId + 1) % 2;
  218. static const bool transpose_result = OpId != 0;
  219. public:
  220. turns_analyser()
  221. : m_previous_turn_ptr(NULL)
  222. , m_previous_operation(overlay::operation_none)
  223. , m_degenerated_turn_ptr(NULL)
  224. , m_collinear_spike_exit(false)
  225. {}
  226. template <typename Result,
  227. typename TurnIt,
  228. typename Geometry,
  229. typename OtherGeometry,
  230. typename BoundaryChecker,
  231. typename OtherBoundaryChecker>
  232. void apply(Result & res, TurnIt it,
  233. Geometry const& geometry,
  234. OtherGeometry const& other_geometry,
  235. BoundaryChecker const& boundary_checker,
  236. OtherBoundaryChecker const& other_boundary_checker)
  237. {
  238. overlay::operation_type const op = it->operations[op_id].operation;
  239. segment_identifier const& seg_id = it->operations[op_id].seg_id;
  240. bool const first_in_range = m_seg_watcher.update(seg_id);
  241. if ( op != overlay::operation_union
  242. && op != overlay::operation_intersection
  243. && op != overlay::operation_blocked )
  244. {
  245. // degenerated turn
  246. if ( op == overlay::operation_continue
  247. && it->method == overlay::method_none
  248. && m_exit_watcher.is_outside(*it)
  249. /*&& ( m_exit_watcher.get_exit_operation() == overlay::operation_none
  250. || ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it) )*/ )
  251. {
  252. // TODO: rewrite the above condition
  253. // WARNING! For spikes the above condition may be TRUE
  254. // When degenerated turns are be marked in a different way than c,c/c
  255. // different condition will be checked
  256. handle_degenerated(res, *it,
  257. geometry, other_geometry,
  258. boundary_checker, other_boundary_checker,
  259. first_in_range);
  260. // TODO: not elegant solution! should be rewritten.
  261. if ( it->operations[op_id].position == overlay::position_back )
  262. {
  263. m_previous_operation = overlay::operation_blocked;
  264. m_exit_watcher.reset_detected_exit();
  265. }
  266. }
  267. return;
  268. }
  269. // reset
  270. m_degenerated_turn_ptr = NULL;
  271. // handle possible exit
  272. bool fake_enter_detected = false;
  273. if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
  274. {
  275. // real exit point - may be multiple
  276. // we know that we entered and now we exit
  277. if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(),
  278. *it,
  279. boundary_checker.strategy()) )
  280. {
  281. m_exit_watcher.reset_detected_exit();
  282. // not the last IP
  283. update<interior, exterior, '1', transpose_result>(res);
  284. }
  285. // fake exit point, reset state
  286. else if ( op == overlay::operation_intersection )
  287. {
  288. m_exit_watcher.reset_detected_exit();
  289. fake_enter_detected = true;
  290. }
  291. }
  292. else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
  293. {
  294. // ignore multiple BLOCKs
  295. if ( op == overlay::operation_blocked )
  296. return;
  297. if ( op == overlay::operation_intersection
  298. && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(),
  299. *it,
  300. boundary_checker.strategy()) )
  301. {
  302. fake_enter_detected = true;
  303. }
  304. m_exit_watcher.reset_detected_exit();
  305. }
  306. // i/i, i/x, i/u
  307. if ( op == overlay::operation_intersection )
  308. {
  309. bool const was_outside = m_exit_watcher.is_outside();
  310. m_exit_watcher.enter(*it);
  311. // interiors overlaps
  312. update<interior, interior, '1', transpose_result>(res);
  313. bool const this_b = it->operations[op_id].position == overlay::position_front // ignore spikes!
  314. && is_ip_on_boundary(it->point, it->operations[op_id],
  315. boundary_checker);
  316. // going inside on boundary point
  317. // may be front only
  318. if ( this_b )
  319. {
  320. // may be front and back
  321. bool const other_b = is_ip_on_boundary(it->point, it->operations[other_op_id],
  322. other_boundary_checker);
  323. // it's also the boundary of the other geometry
  324. if ( other_b )
  325. {
  326. update<boundary, boundary, '0', transpose_result>(res);
  327. }
  328. else
  329. {
  330. update<boundary, interior, '0', transpose_result>(res);
  331. }
  332. }
  333. // going inside on non-boundary point
  334. else
  335. {
  336. // if we didn't enter in the past, we were outside
  337. if ( was_outside
  338. && ! fake_enter_detected
  339. && it->operations[op_id].position != overlay::position_front
  340. && ! m_collinear_spike_exit )
  341. {
  342. update<interior, exterior, '1', transpose_result>(res);
  343. // if it's the first IP then the first point is outside
  344. if ( first_in_range )
  345. {
  346. bool const front_b = boundary_checker.is_endpoint_boundary(
  347. range::front(sub_range(geometry, seg_id)));
  348. // if there is a boundary on the first point
  349. if ( front_b )
  350. {
  351. update<boundary, exterior, '0', transpose_result>(res);
  352. }
  353. }
  354. }
  355. }
  356. m_collinear_spike_exit = false;
  357. }
  358. // u/i, u/u, u/x, x/i, x/u, x/x
  359. else if ( op == overlay::operation_union || op == overlay::operation_blocked )
  360. {
  361. // TODO: is exit watcher still needed?
  362. // couldn't is_collinear and some going inside counter be used instead?
  363. bool const is_collinear = it->operations[op_id].is_collinear;
  364. bool const was_outside = m_exit_watcher.is_outside()
  365. && m_exit_watcher.get_exit_operation() == overlay::operation_none;
  366. // TODO: move the above condition into the exit_watcher?
  367. // to exit we must be currently inside and the current segment must be collinear
  368. if ( !was_outside && is_collinear )
  369. {
  370. m_exit_watcher.exit(*it, false);
  371. // if the position is not set to back it must be a spike
  372. if ( it->operations[op_id].position != overlay::position_back )
  373. {
  374. m_collinear_spike_exit = true;
  375. }
  376. }
  377. bool const op_blocked = op == overlay::operation_blocked;
  378. // we're inside and going out from inside
  379. // possibly going out right now
  380. if ( ! was_outside && is_collinear )
  381. {
  382. if ( op_blocked
  383. && it->operations[op_id].position == overlay::position_back ) // ignore spikes!
  384. {
  385. // check if this is indeed the boundary point
  386. // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
  387. if (boundary_checker.is_endpoint_boundary(it->point))
  388. {
  389. // may be front and back
  390. bool const other_b = is_ip_on_boundary(it->point,
  391. it->operations[other_op_id],
  392. other_boundary_checker);
  393. // it's also the boundary of the other geometry
  394. if ( other_b )
  395. {
  396. update<boundary, boundary, '0', transpose_result>(res);
  397. }
  398. else
  399. {
  400. update<boundary, interior, '0', transpose_result>(res);
  401. }
  402. }
  403. }
  404. }
  405. // we're outside or intersects some segment from the outside
  406. else
  407. {
  408. // if we are truly outside
  409. if ( was_outside
  410. && it->operations[op_id].position != overlay::position_front
  411. && ! m_collinear_spike_exit
  412. /*&& !is_collinear*/ )
  413. {
  414. update<interior, exterior, '1', transpose_result>(res);
  415. }
  416. // boundaries don't overlap - just an optimization
  417. if ( it->method == overlay::method_crosses )
  418. {
  419. // the L1 is going from one side of the L2 to the other through the point
  420. update<interior, interior, '0', transpose_result>(res);
  421. // it's the first point in range
  422. if ( first_in_range )
  423. {
  424. bool const front_b = boundary_checker.is_endpoint_boundary(
  425. range::front(sub_range(geometry, seg_id)));
  426. // if there is a boundary on the first point
  427. if ( front_b )
  428. {
  429. update<boundary, exterior, '0', transpose_result>(res);
  430. }
  431. }
  432. }
  433. // method other than crosses, check more conditions
  434. else
  435. {
  436. bool const this_b = is_ip_on_boundary(it->point,
  437. it->operations[op_id],
  438. boundary_checker);
  439. bool const other_b = is_ip_on_boundary(it->point,
  440. it->operations[other_op_id],
  441. other_boundary_checker);
  442. // if current IP is on boundary of the geometry
  443. if ( this_b )
  444. {
  445. // it's also the boundary of the other geometry
  446. if ( other_b )
  447. {
  448. update<boundary, boundary, '0', transpose_result>(res);
  449. }
  450. else
  451. {
  452. update<boundary, interior, '0', transpose_result>(res);
  453. }
  454. }
  455. // if current IP is not on boundary of the geometry
  456. else
  457. {
  458. // it's also the boundary of the other geometry
  459. if ( other_b )
  460. {
  461. update<interior, boundary, '0', transpose_result>(res);
  462. }
  463. else
  464. {
  465. update<interior, interior, '0', transpose_result>(res);
  466. }
  467. }
  468. // first IP on the last segment point - this means that the first point is outside
  469. if ( first_in_range
  470. && ( !this_b || op_blocked )
  471. && was_outside
  472. && it->operations[op_id].position != overlay::position_front
  473. && ! m_collinear_spike_exit
  474. /*&& !is_collinear*/ )
  475. {
  476. bool const front_b = boundary_checker.is_endpoint_boundary(
  477. range::front(sub_range(geometry, seg_id)));
  478. // if there is a boundary on the first point
  479. if ( front_b )
  480. {
  481. update<boundary, exterior, '0', transpose_result>(res);
  482. }
  483. }
  484. }
  485. }
  486. }
  487. // store ref to previously analysed (valid) turn
  488. m_previous_turn_ptr = boost::addressof(*it);
  489. // and previously analysed (valid) operation
  490. m_previous_operation = op;
  491. }
  492. // Called for last
  493. template <typename Result,
  494. typename TurnIt,
  495. typename Geometry,
  496. typename OtherGeometry,
  497. typename BoundaryChecker,
  498. typename OtherBoundaryChecker>
  499. void apply(Result & res,
  500. TurnIt first, TurnIt last,
  501. Geometry const& geometry,
  502. OtherGeometry const& /*other_geometry*/,
  503. BoundaryChecker const& boundary_checker,
  504. OtherBoundaryChecker const& /*other_boundary_checker*/)
  505. {
  506. boost::ignore_unused(first, last);
  507. //BOOST_GEOMETRY_ASSERT( first != last );
  508. // here, the possible exit is the real one
  509. // we know that we entered and now we exit
  510. if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
  511. ||*/ m_previous_operation == overlay::operation_union
  512. || m_degenerated_turn_ptr )
  513. {
  514. update<interior, exterior, '1', transpose_result>(res);
  515. BOOST_GEOMETRY_ASSERT(first != last);
  516. const TurnInfo * turn_ptr = NULL;
  517. if ( m_degenerated_turn_ptr )
  518. turn_ptr = m_degenerated_turn_ptr;
  519. else if ( m_previous_turn_ptr )
  520. turn_ptr = m_previous_turn_ptr;
  521. if ( turn_ptr )
  522. {
  523. segment_identifier const& prev_seg_id = turn_ptr->operations[op_id].seg_id;
  524. //BOOST_GEOMETRY_ASSERT(!boost::empty(sub_range(geometry, prev_seg_id)));
  525. bool const prev_back_b = boundary_checker.is_endpoint_boundary(
  526. range::back(sub_range(geometry, prev_seg_id)));
  527. // if there is a boundary on the last point
  528. if ( prev_back_b )
  529. {
  530. update<boundary, exterior, '0', transpose_result>(res);
  531. }
  532. }
  533. }
  534. // Just in case,
  535. // reset exit watcher before the analysis of the next Linestring
  536. // note that if there are some enters stored there may be some error above
  537. m_exit_watcher.reset();
  538. m_previous_turn_ptr = NULL;
  539. m_previous_operation = overlay::operation_none;
  540. m_degenerated_turn_ptr = NULL;
  541. // actually if this is set to true here there is some error
  542. // in get_turns_ll or relate_ll, an assert could be checked here
  543. m_collinear_spike_exit = false;
  544. }
  545. template <typename Result,
  546. typename Turn,
  547. typename Geometry,
  548. typename OtherGeometry,
  549. typename BoundaryChecker,
  550. typename OtherBoundaryChecker>
  551. void handle_degenerated(Result & res,
  552. Turn const& turn,
  553. Geometry const& geometry,
  554. OtherGeometry const& other_geometry,
  555. BoundaryChecker const& boundary_checker,
  556. OtherBoundaryChecker const& other_boundary_checker,
  557. bool first_in_range)
  558. {
  559. auto const& ls1 = detail::single_geometry(geometry, turn.operations[op_id].seg_id);
  560. auto const& ls2 = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id);
  561. // only one of those should be true:
  562. if ( turn.operations[op_id].position == overlay::position_front )
  563. {
  564. // valid, point-sized
  565. if ( boost::size(ls2) == 2 )
  566. {
  567. bool const front_b = boundary_checker.is_endpoint_boundary(turn.point);
  568. if ( front_b )
  569. {
  570. update<boundary, interior, '0', transpose_result>(res);
  571. }
  572. else
  573. {
  574. update<interior, interior, '0', transpose_result>(res);
  575. }
  576. // operation 'c' should be last for the same IP so we know that the next point won't be the same
  577. update<interior, exterior, '1', transpose_result>(res);
  578. m_degenerated_turn_ptr = boost::addressof(turn);
  579. }
  580. }
  581. else if ( turn.operations[op_id].position == overlay::position_back )
  582. {
  583. // valid, point-sized
  584. if ( boost::size(ls2) == 2 )
  585. {
  586. update<interior, exterior, '1', transpose_result>(res);
  587. bool const back_b = boundary_checker.is_endpoint_boundary(turn.point);
  588. if ( back_b )
  589. {
  590. update<boundary, interior, '0', transpose_result>(res);
  591. }
  592. else
  593. {
  594. update<interior, interior, '0', transpose_result>(res);
  595. }
  596. if ( first_in_range )
  597. {
  598. //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1));
  599. bool const front_b = boundary_checker.is_endpoint_boundary(
  600. range::front(ls1));
  601. if ( front_b )
  602. {
  603. update<boundary, exterior, '0', transpose_result>(res);
  604. }
  605. }
  606. }
  607. }
  608. else if ( turn.operations[op_id].position == overlay::position_middle
  609. && turn.operations[other_op_id].position == overlay::position_middle )
  610. {
  611. update<interior, interior, '0', transpose_result>(res);
  612. // here we don't know which one is degenerated
  613. bool const is_point1 = boost::size(ls1) == 2
  614. && equals::equals_point_point(range::front(ls1),
  615. range::back(ls1),
  616. boundary_checker.strategy());
  617. bool const is_point2 = boost::size(ls2) == 2
  618. && equals::equals_point_point(range::front(ls2),
  619. range::back(ls2),
  620. other_boundary_checker.strategy());
  621. // if the second one is degenerated
  622. if ( !is_point1 && is_point2 )
  623. {
  624. update<interior, exterior, '1', transpose_result>(res);
  625. if ( first_in_range )
  626. {
  627. //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1));
  628. bool const front_b = boundary_checker.is_endpoint_boundary(
  629. range::front(ls1));
  630. if ( front_b )
  631. {
  632. update<boundary, exterior, '0', transpose_result>(res);
  633. }
  634. }
  635. m_degenerated_turn_ptr = boost::addressof(turn);
  636. }
  637. }
  638. // NOTE: other.position == front and other.position == back
  639. // will be handled later, for the other geometry
  640. }
  641. private:
  642. exit_watcher<TurnInfo, OpId> m_exit_watcher;
  643. segment_watcher<same_single> m_seg_watcher;
  644. const TurnInfo * m_previous_turn_ptr;
  645. overlay::operation_type m_previous_operation;
  646. const TurnInfo * m_degenerated_turn_ptr;
  647. bool m_collinear_spike_exit;
  648. };
  649. template <typename Result,
  650. typename TurnIt,
  651. typename Analyser,
  652. typename Geometry,
  653. typename OtherGeometry,
  654. typename BoundaryChecker,
  655. typename OtherBoundaryChecker>
  656. static inline void analyse_each_turn(Result & res,
  657. Analyser & analyser,
  658. TurnIt first, TurnIt last,
  659. Geometry const& geometry,
  660. OtherGeometry const& other_geometry,
  661. BoundaryChecker const& boundary_checker,
  662. OtherBoundaryChecker const& other_boundary_checker)
  663. {
  664. if ( first == last )
  665. return;
  666. for ( TurnIt it = first ; it != last ; ++it )
  667. {
  668. analyser.apply(res, it,
  669. geometry, other_geometry,
  670. boundary_checker, other_boundary_checker);
  671. if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
  672. return;
  673. }
  674. analyser.apply(res, first, last,
  675. geometry, other_geometry,
  676. boundary_checker, other_boundary_checker);
  677. }
  678. };
  679. }} // namespace detail::relate
  680. #endif // DOXYGEN_NO_DETAIL
  681. }} // namespace boost::geometry
  682. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP